package orgpolicy

import (
	"strings"

	"github.com/ultisuite/ulti-backend/internal/auth"
)

type IdentityProviderPolicy struct {
	ID                    string
	Slug                  string
	Type                  string
	Enabled               bool
	AllowedEmailDomains   []string
	AllowedIdentities     []string
	AllowedOrganizations  []string
}

type AuthAccessPolicy struct {
	AllowSelfEnrollment bool
	Providers           []IdentityProviderPolicy
}

func (p AuthAccessPolicy) AllowsIdentity(email string, claims *auth.Claims) bool {
	enabled := make([]IdentityProviderPolicy, 0)
	for _, provider := range p.Providers {
		if provider.Enabled {
			enabled = append(enabled, provider)
		}
	}
	if len(enabled) == 0 {
		return true
	}

	if claims != nil && strings.TrimSpace(claims.Source) != "" {
		for _, provider := range enabled {
			if provider.Slug == claims.Source && providerAllows(provider, email, claims) {
				return true
			}
		}
	}

	hasRestrictions := false
	for _, provider := range enabled {
		if providerHasRestrictions(provider) {
			hasRestrictions = true
			if providerAllows(provider, email, claims) {
				return true
			}
		}
	}
	if !hasRestrictions {
		return true
	}
	return false
}

func providerHasRestrictions(provider IdentityProviderPolicy) bool {
	return len(provider.AllowedEmailDomains) > 0 ||
		len(provider.AllowedIdentities) > 0 ||
		len(provider.AllowedOrganizations) > 0
}

func providerAllows(provider IdentityProviderPolicy, email string, claims *auth.Claims) bool {
	if len(provider.AllowedIdentities) > 0 {
		if !containsFold(provider.AllowedIdentities, email) {
			return false
		}
	}
	if len(provider.AllowedEmailDomains) > 0 {
		if !emailDomainAllowed(email, provider.AllowedEmailDomains) {
			return false
		}
	}
	if len(provider.AllowedOrganizations) > 0 {
		if claims == nil || !organizationAllowed(claims, provider.AllowedOrganizations) {
			return false
		}
	}
	return true
}

func emailDomainAllowed(email string, domains []string) bool {
	email = strings.ToLower(strings.TrimSpace(email))
	at := strings.LastIndex(email, "@")
	if at < 0 {
		return false
	}
	domain := email[at+1:]
	for _, allowed := range domains {
		allowed = strings.ToLower(strings.TrimSpace(strings.TrimPrefix(allowed, "@")))
		if allowed == "" {
			continue
		}
		if domain == allowed || strings.HasSuffix(domain, "."+allowed) {
			return true
		}
	}
	return false
}

func organizationAllowed(claims *auth.Claims, orgs []string) bool {
	candidates := []string{
		claims.HD,
		claims.TID,
		claims.Org,
	}
	for _, org := range orgs {
		org = strings.TrimSpace(org)
		if org == "" {
			continue
		}
		for _, candidate := range candidates {
			if strings.EqualFold(strings.TrimSpace(candidate), org) {
				return true
			}
		}
	}
	return false
}

func containsFold(list []string, value string) bool {
	value = strings.ToLower(strings.TrimSpace(value))
	for _, item := range list {
		if strings.ToLower(strings.TrimSpace(item)) == value {
			return true
		}
	}
	return false
}

func stringSlice(v any) []string {
	raw, ok := v.([]any)
	if !ok {
		return nil
	}
	out := make([]string, 0, len(raw))
	for _, item := range raw {
		if s, ok := item.(string); ok && strings.TrimSpace(s) != "" {
			out = append(out, strings.TrimSpace(s))
		}
	}
	return out
}
