package policy

import (
	"context"
	"encoding/json"

	"github.com/google/uuid"
)

// Auditor provides audit logging functionality for the policy system.
type Auditor struct {
	store *Store
}

// NewAuditor creates a new Auditor instance.
func NewAuditor(store *Store) *Auditor {
	return &Auditor{store: store}
}

// LogChange logs a policy change to the audit trail.
func (a *Auditor) LogChange(ctx context.Context, action AuditAction, entityType AuditEntityType, entityID *uuid.UUID, oldValue, newValue interface{}, userEmail, ipAddress, userAgent *string) error {
	entry := &PolicyAuditLog{
		Action:     action,
		EntityType: entityType,
		EntityID:   entityID,
		UserEmail:  userEmail,
		IPAddress:  ipAddress,
		UserAgent:  userAgent,
	}

	if oldValue != nil {
		entry.OldValue = toJSON(oldValue)
	}
	if newValue != nil {
		entry.NewValue = toJSON(newValue)
	}

	return a.store.CreateAuditLog(ctx, entry)
}

// LogBlocked logs a blocked URL to the blocked content log.
func (a *Auditor) LogBlocked(ctx context.Context, url, domain string, reason BlockReason, ruleID *uuid.UUID, details map[string]interface{}) error {
	entry := &BlockedContentLog{
		URL:           url,
		Domain:        domain,
		BlockReason:   reason,
		MatchedRuleID: ruleID,
	}

	if details != nil {
		entry.Details = toJSON(details)
	}

	return a.store.CreateBlockedContentLog(ctx, entry)
}

// =============================================================================
// CONVENIENCE METHODS
// =============================================================================

// LogPolicyCreated logs a policy creation event.
func (a *Auditor) LogPolicyCreated(ctx context.Context, policy *SourcePolicy, userEmail *string) error {
	return a.LogChange(ctx, AuditActionCreate, AuditEntitySourcePolicy, &policy.ID, nil, policy, userEmail, nil, nil)
}

// LogPolicyUpdated logs a policy update event.
func (a *Auditor) LogPolicyUpdated(ctx context.Context, oldPolicy, newPolicy *SourcePolicy, userEmail *string) error {
	return a.LogChange(ctx, AuditActionUpdate, AuditEntitySourcePolicy, &newPolicy.ID, oldPolicy, newPolicy, userEmail, nil, nil)
}

// LogPolicyDeleted logs a policy deletion event.
func (a *Auditor) LogPolicyDeleted(ctx context.Context, policy *SourcePolicy, userEmail *string) error {
	return a.LogChange(ctx, AuditActionDelete, AuditEntitySourcePolicy, &policy.ID, policy, nil, userEmail, nil, nil)
}

// LogPolicyActivated logs a policy activation event.
func (a *Auditor) LogPolicyActivated(ctx context.Context, policy *SourcePolicy, userEmail *string) error {
	return a.LogChange(ctx, AuditActionActivate, AuditEntitySourcePolicy, &policy.ID, nil, policy, userEmail, nil, nil)
}

// LogPolicyDeactivated logs a policy deactivation event.
func (a *Auditor) LogPolicyDeactivated(ctx context.Context, policy *SourcePolicy, userEmail *string) error {
	return a.LogChange(ctx, AuditActionDeactivate, AuditEntitySourcePolicy, &policy.ID, policy, nil, userEmail, nil, nil)
}

// LogSourceCreated logs a source creation event.
func (a *Auditor) LogSourceCreated(ctx context.Context, source *AllowedSource, userEmail *string) error {
	return a.LogChange(ctx, AuditActionCreate, AuditEntityAllowedSource, &source.ID, nil, source, userEmail, nil, nil)
}

// LogSourceUpdated logs a source update event.
func (a *Auditor) LogSourceUpdated(ctx context.Context, oldSource, newSource *AllowedSource, userEmail *string) error {
	return a.LogChange(ctx, AuditActionUpdate, AuditEntityAllowedSource, &newSource.ID, oldSource, newSource, userEmail, nil, nil)
}

// LogSourceDeleted logs a source deletion event.
func (a *Auditor) LogSourceDeleted(ctx context.Context, source *AllowedSource, userEmail *string) error {
	return a.LogChange(ctx, AuditActionDelete, AuditEntityAllowedSource, &source.ID, source, nil, userEmail, nil, nil)
}

// LogOperationUpdated logs an operation permission update event.
func (a *Auditor) LogOperationUpdated(ctx context.Context, oldOp, newOp *OperationPermission, userEmail *string) error {
	return a.LogChange(ctx, AuditActionUpdate, AuditEntityOperationPermission, &newOp.ID, oldOp, newOp, userEmail, nil, nil)
}

// LogPIIRuleCreated logs a PII rule creation event.
func (a *Auditor) LogPIIRuleCreated(ctx context.Context, rule *PIIRule, userEmail *string) error {
	return a.LogChange(ctx, AuditActionCreate, AuditEntityPIIRule, &rule.ID, nil, rule, userEmail, nil, nil)
}

// LogPIIRuleUpdated logs a PII rule update event.
func (a *Auditor) LogPIIRuleUpdated(ctx context.Context, oldRule, newRule *PIIRule, userEmail *string) error {
	return a.LogChange(ctx, AuditActionUpdate, AuditEntityPIIRule, &newRule.ID, oldRule, newRule, userEmail, nil, nil)
}

// LogPIIRuleDeleted logs a PII rule deletion event.
func (a *Auditor) LogPIIRuleDeleted(ctx context.Context, rule *PIIRule, userEmail *string) error {
	return a.LogChange(ctx, AuditActionDelete, AuditEntityPIIRule, &rule.ID, rule, nil, userEmail, nil, nil)
}

// LogContentBlocked logs a blocked content event with details.
func (a *Auditor) LogContentBlocked(ctx context.Context, url, domain string, reason BlockReason, matchedPatterns []string, ruleID *uuid.UUID) error {
	details := map[string]interface{}{
		"matched_patterns": matchedPatterns,
	}
	return a.LogBlocked(ctx, url, domain, reason, ruleID, details)
}

// LogPIIBlocked logs content blocked due to PII detection.
func (a *Auditor) LogPIIBlocked(ctx context.Context, url, domain string, matches []PIIMatch) error {
	matchDetails := make([]map[string]interface{}, len(matches))
	var ruleID *uuid.UUID

	for i, m := range matches {
		matchDetails[i] = map[string]interface{}{
			"rule_name": m.RuleName,
			"severity":  m.Severity,
			"match":     maskPII(m.Match), // Mask the actual PII in logs
		}
		if ruleID == nil {
			ruleID = &m.RuleID
		}
	}

	details := map[string]interface{}{
		"pii_matches": matchDetails,
		"match_count": len(matches),
	}

	return a.LogBlocked(ctx, url, domain, BlockReasonPIIDetected, ruleID, details)
}

// =============================================================================
// HELPERS
// =============================================================================

// toJSON converts a value to JSON.
func toJSON(v interface{}) json.RawMessage {
	data, err := json.Marshal(v)
	if err != nil {
		return nil
	}
	return data
}

// maskPII masks PII data for safe logging.
func maskPII(pii string) string {
	if len(pii) <= 4 {
		return "****"
	}
	// Show first 2 and last 2 characters
	return pii[:2] + "****" + pii[len(pii)-2:]
}

// =============================================================================
// AUDIT REPORT GENERATION
// =============================================================================

// AuditReport represents an audit report for compliance.
type AuditReport struct {
	GeneratedAt    string              `json:"generated_at"`
	PeriodStart    string              `json:"period_start"`
	PeriodEnd      string              `json:"period_end"`
	Summary        AuditReportSummary  `json:"summary"`
	PolicyChanges  []PolicyAuditLog    `json:"policy_changes"`
	BlockedContent []BlockedContentLog `json:"blocked_content"`
	Stats          *PolicyStats        `json:"stats"`
}

// AuditReportSummary contains summary statistics for the audit report.
type AuditReportSummary struct {
	TotalPolicyChanges int            `json:"total_policy_changes"`
	TotalBlocked       int            `json:"total_blocked"`
	ChangesByAction    map[string]int `json:"changes_by_action"`
	BlocksByReason     map[string]int `json:"blocks_by_reason"`
}

// GenerateAuditReport generates a compliance audit report.
func (a *Auditor) GenerateAuditReport(ctx context.Context, filter *AuditLogFilter, blockedFilter *BlockedContentFilter) (*AuditReport, error) {
	// Get audit logs
	auditLogs, _, err := a.store.ListAuditLogs(ctx, filter)
	if err != nil {
		return nil, err
	}

	// Get blocked content
	blockedLogs, _, err := a.store.ListBlockedContent(ctx, blockedFilter)
	if err != nil {
		return nil, err
	}

	// Get stats
	stats, err := a.store.GetStats(ctx)
	if err != nil {
		return nil, err
	}

	// Build summary
	summary := AuditReportSummary{
		TotalPolicyChanges: len(auditLogs),
		TotalBlocked:       len(blockedLogs),
		ChangesByAction:    make(map[string]int),
		BlocksByReason:     make(map[string]int),
	}

	for _, log := range auditLogs {
		summary.ChangesByAction[string(log.Action)]++
	}

	for _, log := range blockedLogs {
		summary.BlocksByReason[string(log.BlockReason)]++
	}

	// Build report
	periodStart := ""
	periodEnd := ""
	if filter.FromDate != nil {
		periodStart = filter.FromDate.Format("2006-01-02")
	}
	if filter.ToDate != nil {
		periodEnd = filter.ToDate.Format("2006-01-02")
	}

	report := &AuditReport{
		GeneratedAt:    uuid.New().String()[:19], // Timestamp placeholder
		PeriodStart:    periodStart,
		PeriodEnd:      periodEnd,
		Summary:        summary,
		PolicyChanges:  auditLogs,
		BlockedContent: blockedLogs,
		Stats:          stats,
	}

	return report, nil
}
