package middleware

import (
	"regexp"
	"strings"
)

// PIIPattern defines a pattern for identifying PII.
type PIIPattern struct {
	Name        string
	Pattern     *regexp.Regexp
	Replacement string
}

// PIIRedactor redacts personally identifiable information from strings.
type PIIRedactor struct {
	patterns []*PIIPattern
}

// Pre-compiled patterns for common PII types
var (
	emailPattern = regexp.MustCompile(`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b`)
	ipv4Pattern  = regexp.MustCompile(`\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b`)
	ipv6Pattern  = regexp.MustCompile(`\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b`)
	phonePattern = regexp.MustCompile(`(?:\+49|0049)[\s.-]?\d{2,4}[\s.-]?\d{3,8}|\b0\d{2,4}[\s.-]?\d{3,8}\b`)
	ibanPattern  = regexp.MustCompile(`(?i)\b[A-Z]{2}\d{2}[\s]?(?:\d{4}[\s]?){3,5}\d{1,4}\b`)
	uuidPattern  = regexp.MustCompile(`(?i)\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b`)
	namePattern  = regexp.MustCompile(`\b(?:Herr|Frau|Hr\.|Fr\.)\s+[A-ZÄÖÜ][a-zäöüß]+(?:\s+[A-ZÄÖÜ][a-zäöüß]+)?\b`)
)

// DefaultPIIPatterns returns the default set of PII patterns.
func DefaultPIIPatterns() []*PIIPattern {
	return []*PIIPattern{
		{Name: "email", Pattern: emailPattern, Replacement: "[EMAIL_REDACTED]"},
		{Name: "ip_v4", Pattern: ipv4Pattern, Replacement: "[IP_REDACTED]"},
		{Name: "ip_v6", Pattern: ipv6Pattern, Replacement: "[IP_REDACTED]"},
		{Name: "phone", Pattern: phonePattern, Replacement: "[PHONE_REDACTED]"},
	}
}

// AllPIIPatterns returns all available PII patterns.
func AllPIIPatterns() []*PIIPattern {
	return []*PIIPattern{
		{Name: "email", Pattern: emailPattern, Replacement: "[EMAIL_REDACTED]"},
		{Name: "ip_v4", Pattern: ipv4Pattern, Replacement: "[IP_REDACTED]"},
		{Name: "ip_v6", Pattern: ipv6Pattern, Replacement: "[IP_REDACTED]"},
		{Name: "phone", Pattern: phonePattern, Replacement: "[PHONE_REDACTED]"},
		{Name: "iban", Pattern: ibanPattern, Replacement: "[IBAN_REDACTED]"},
		{Name: "uuid", Pattern: uuidPattern, Replacement: "[UUID_REDACTED]"},
		{Name: "name", Pattern: namePattern, Replacement: "[NAME_REDACTED]"},
	}
}

// NewPIIRedactor creates a new PII redactor with the given patterns.
func NewPIIRedactor(patterns []*PIIPattern) *PIIRedactor {
	if patterns == nil {
		patterns = DefaultPIIPatterns()
	}
	return &PIIRedactor{patterns: patterns}
}

// NewDefaultPIIRedactor creates a PII redactor with default patterns.
func NewDefaultPIIRedactor() *PIIRedactor {
	return NewPIIRedactor(DefaultPIIPatterns())
}

// Redact removes PII from the given text.
func (r *PIIRedactor) Redact(text string) string {
	if text == "" {
		return text
	}

	result := text
	for _, pattern := range r.patterns {
		result = pattern.Pattern.ReplaceAllString(result, pattern.Replacement)
	}
	return result
}

// ContainsPII checks if the text contains any PII.
func (r *PIIRedactor) ContainsPII(text string) bool {
	if text == "" {
		return false
	}

	for _, pattern := range r.patterns {
		if pattern.Pattern.MatchString(text) {
			return true
		}
	}
	return false
}

// PIIFinding represents a found PII instance.
type PIIFinding struct {
	Type  string
	Match string
	Start int
	End   int
}

// FindPII finds all PII in the text.
func (r *PIIRedactor) FindPII(text string) []PIIFinding {
	if text == "" {
		return nil
	}

	var findings []PIIFinding
	for _, pattern := range r.patterns {
		matches := pattern.Pattern.FindAllStringIndex(text, -1)
		for _, match := range matches {
			findings = append(findings, PIIFinding{
				Type:  pattern.Name,
				Match: text[match[0]:match[1]],
				Start: match[0],
				End:   match[1],
			})
		}
	}
	return findings
}

// Default module-level redactor
var defaultRedactor = NewDefaultPIIRedactor()

// RedactPII is a convenience function that uses the default redactor.
func RedactPII(text string) string {
	return defaultRedactor.Redact(text)
}

// ContainsPIIDefault checks if text contains PII using default patterns.
func ContainsPIIDefault(text string) bool {
	return defaultRedactor.ContainsPII(text)
}

// RedactMap redacts PII from all string values in a map.
func RedactMap(data map[string]interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	for key, value := range data {
		switch v := value.(type) {
		case string:
			result[key] = RedactPII(v)
		case map[string]interface{}:
			result[key] = RedactMap(v)
		case []interface{}:
			result[key] = redactSlice(v)
		default:
			result[key] = v
		}
	}
	return result
}

func redactSlice(data []interface{}) []interface{} {
	result := make([]interface{}, len(data))
	for i, value := range data {
		switch v := value.(type) {
		case string:
			result[i] = RedactPII(v)
		case map[string]interface{}:
			result[i] = RedactMap(v)
		case []interface{}:
			result[i] = redactSlice(v)
		default:
			result[i] = v
		}
	}
	return result
}

// SafeLogString creates a safe-to-log version of sensitive data.
// Use this for logging user-related information.
func SafeLogString(format string, args ...interface{}) string {
	// Convert args to strings and redact
	safeArgs := make([]interface{}, len(args))
	for i, arg := range args {
		switch v := arg.(type) {
		case string:
			safeArgs[i] = RedactPII(v)
		case error:
			safeArgs[i] = RedactPII(v.Error())
		default:
			safeArgs[i] = arg
		}
	}

	// Note: We can't use fmt.Sprintf here due to the variadic nature
	// Instead, we redact the result
	result := format
	for _, arg := range safeArgs {
		if s, ok := arg.(string); ok {
			result = strings.Replace(result, "%s", s, 1)
			result = strings.Replace(result, "%v", s, 1)
		}
	}
	return RedactPII(result)
}
