package authidentity

import (
	"fmt"
	"net/mail"
	"strings"
)

type Kind string

const (
	KindEmail  Kind = "email"
	KindMobile Kind = "mobile"
)

type LoginIdentifier struct {
	Kind  Kind
	Value string
}

func NormalizeEmail(email string) (string, error) {
	parsedAddress, err := mail.ParseAddress(strings.TrimSpace(email))
	if err != nil {
		return "", fmt.Errorf("invalid email address")
	}
	return strings.ToLower(parsedAddress.Address), nil
}

func NormalizeStrictEmail(email string) (string, error) {
	trimmed := strings.TrimSpace(email)
	if trimmed == "" {
		return "", fmt.Errorf("invalid email address")
	}
	parsedAddress, err := mail.ParseAddress(trimmed)
	if err != nil || strings.TrimSpace(parsedAddress.Name) != "" {
		return "", fmt.Errorf("invalid email address")
	}
	if !strings.EqualFold(parsedAddress.Address, trimmed) {
		return "", fmt.Errorf("invalid email address")
	}
	normalized := strings.ToLower(parsedAddress.Address)
	if len(normalized) > 320 {
		return "", fmt.Errorf("invalid email address")
	}
	return normalized, nil
}

func NormalizeEmailAllowlistPattern(pattern string) (string, error) {
	trimmed := strings.ToLower(strings.TrimSpace(pattern))
	if trimmed == "" {
		return "", fmt.Errorf("invalid allowlist pattern")
	}
	if strings.HasPrefix(trimmed, "@") {
		domain := strings.TrimPrefix(trimmed, "@")
		if domain == "" || strings.Contains(domain, "@") {
			return "", fmt.Errorf("invalid allowlist pattern")
		}
		normalizedDomain, err := NormalizeEmail("noreply@" + domain)
		if err != nil {
			return "", fmt.Errorf("invalid allowlist pattern")
		}
		return "@" + strings.TrimPrefix(normalizedDomain, "noreply@"), nil
	}
	return NormalizeEmail(trimmed)
}

func EmailMatchesAllowlistPattern(email string, pattern string) bool {
	normalizedEmail, err := NormalizeEmail(email)
	if err != nil {
		return false
	}
	normalizedPattern, err := NormalizeEmailAllowlistPattern(pattern)
	if err != nil {
		return false
	}
	if strings.HasPrefix(normalizedPattern, "@") {
		return strings.HasSuffix(normalizedEmail, normalizedPattern)
	}
	return normalizedEmail == normalizedPattern
}

func NormalizeMobile(mobile string) (string, error) {
	trimmed := strings.TrimSpace(mobile)
	if trimmed == "" {
		return "", fmt.Errorf("invalid mobile number")
	}

	var builder strings.Builder
	for index, r := range trimmed {
		switch {
		case r >= '0' && r <= '9':
			builder.WriteRune(r)
		case r == '+' && index == 0:
			builder.WriteRune(r)
		case r == ' ' || r == '-' || r == '(' || r == ')' || r == '.':
			continue
		default:
			return "", fmt.Errorf("invalid mobile number")
		}
	}

	normalized := builder.String()
	digits := strings.TrimPrefix(normalized, "+")
	if len(digits) < 6 || len(digits) > 15 {
		return "", fmt.Errorf("invalid mobile number")
	}
	if !strings.HasPrefix(normalized, "+") {
		if len(digits) == 11 && strings.HasPrefix(digits, "1") {
			return "+86" + digits, nil
		}
		if len(digits) == 13 && strings.HasPrefix(digits, "861") {
			return "+" + digits, nil
		}
		return "", fmt.Errorf("invalid mobile number")
	}
	if strings.HasPrefix(digits, "0") {
		return "", fmt.Errorf("invalid mobile number")
	}
	return normalized, nil
}

func ResolveLoginIdentifier(raw string) (LoginIdentifier, error) {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return LoginIdentifier{}, fmt.Errorf("identifier is required")
	}
	if strings.Contains(trimmed, "@") {
		resolvedEmail, err := NormalizeEmail(trimmed)
		if err != nil {
			return LoginIdentifier{}, err
		}
		return LoginIdentifier{Kind: KindEmail, Value: resolvedEmail}, nil
	}
	resolvedMobile, err := NormalizeMobile(trimmed)
	if err != nil {
		return LoginIdentifier{}, err
	}
	return LoginIdentifier{Kind: KindMobile, Value: resolvedMobile}, nil
}

func ResolveInviteIdentifier(raw string) (LoginIdentifier, error) {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return LoginIdentifier{}, fmt.Errorf("identifier is required")
	}

	resolvedEmail, emailErr := NormalizeStrictEmail(trimmed)
	resolvedMobile, mobileErr := NormalizeMobile(trimmed)
	emailValid := emailErr == nil
	mobileValid := mobileErr == nil

	switch {
	case emailValid && mobileValid:
		return LoginIdentifier{}, fmt.Errorf("invite identifier is ambiguous")
	case emailValid:
		return LoginIdentifier{Kind: KindEmail, Value: resolvedEmail}, nil
	case mobileValid:
		return LoginIdentifier{Kind: KindMobile, Value: resolvedMobile}, nil
	default:
		return LoginIdentifier{}, fmt.Errorf("identifier must be a valid email or mobile number")
	}
}

func RedactMobile(mobile string) string {
	trimmed := strings.TrimSpace(mobile)
	if trimmed == "" {
		return ""
	}

	prefix := ""
	if strings.HasPrefix(trimmed, "+") {
		prefix = "+"
		trimmed = trimmed[1:]
	}

	runes := []rune(trimmed)
	if len(runes) <= 4 {
		if len(runes) <= 2 {
			return prefix + strings.Repeat("*", len(runes))
		}
		return prefix + string(runes[:1]) + strings.Repeat("*", len(runes)-1)
	}

	visiblePrefix := 4
	if len(runes) < visiblePrefix+2 {
		visiblePrefix = len(runes) - 2
	}
	if visiblePrefix < 1 {
		visiblePrefix = 1
	}
	visibleSuffix := 2
	if len(runes) <= visiblePrefix+visibleSuffix {
		visibleSuffix = 1
	}
	maskedLength := len(runes) - visiblePrefix - visibleSuffix
	if maskedLength < 1 {
		maskedLength = 1
		if visiblePrefix+visibleSuffix >= len(runes) {
			visiblePrefix = len(runes) - 1
			visibleSuffix = 0
		}
	}

	masked := prefix + string(runes[:visiblePrefix]) + strings.Repeat("*", maskedLength)
	if visibleSuffix > 0 {
		masked += string(runes[len(runes)-visibleSuffix:])
	}
	return masked
}
