package security

import (
	"fmt"
	"sort"
	"strings"

	"github.com/niels/banking-app/backend/internal/domain"
)

const (
	ScopeSelfRead        = "self:read"
	ScopeMoneyRead       = "money:read"
	ScopeMoneyWrite      = "money:write"
	ScopeCardsWrite      = "cards:write"
	ScopeCryptoWrite     = "crypto:write"
	ScopeAdminRead       = "admin:read"
	ScopeAdminWrite      = "admin:write"
	ScopeComplianceRead  = "compliance:read"
	ScopeComplianceWrite = "compliance:write"
	ScopeLedgerRead      = "ledger:read"
	ScopeAuditRead       = "audit:read"
)

func ScopesForRole(role string) []string {
	switch role {
	case "admin":
		return []string{
			ScopeSelfRead,
			ScopeAdminRead,
		}
	default:
		return []string{
			ScopeSelfRead,
			ScopeMoneyRead,
			ScopeMoneyWrite,
			ScopeCardsWrite,
			ScopeCryptoWrite,
		}
	}
}

func AllAdminScopes() []string {
	return []string{
		ScopeSelfRead,
		ScopeMoneyRead,
		ScopeMoneyWrite,
		ScopeCardsWrite,
		ScopeCryptoWrite,
		ScopeAdminRead,
		ScopeAdminWrite,
		ScopeComplianceRead,
		ScopeComplianceWrite,
		ScopeLedgerRead,
		ScopeAuditRead,
	}
}

func KnownScopes() []string {
	return append([]string{}, AllAdminScopes()...)
}

func NormalizeScopes(scopes []string) ([]string, error) {
	known := map[string]bool{}
	for _, scope := range KnownScopes() {
		known[scope] = true
	}

	seen := map[string]bool{}
	normalized := []string{}
	for _, raw := range scopes {
		scope := strings.ToLower(strings.TrimSpace(raw))
		if scope == "" {
			continue
		}
		if !known[scope] {
			return nil, fmt.Errorf("%w: unknown scope %q", domain.ErrValidation, scope)
		}
		if !seen[scope] {
			seen[scope] = true
			normalized = append(normalized, scope)
		}
	}
	sort.Strings(normalized)
	return normalized, nil
}

func MergeScopes(groups ...[]string) []string {
	seen := map[string]bool{}
	merged := []string{}
	for _, group := range groups {
		for _, raw := range group {
			scope := strings.ToLower(strings.TrimSpace(raw))
			if scope == "" || seen[scope] {
				continue
			}
			seen[scope] = true
			merged = append(merged, scope)
		}
	}
	sort.Strings(merged)
	return merged
}

func HasScope(scopes []string, required string) bool {
	for _, scope := range scopes {
		if scope == required {
			return true
		}
	}
	return false
}
