// File Path: monorepo/cloud/maplepress-backend/internal/service/securityevent/logger.go
package securityevent

import (
	"context"

	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/securityevent"
)

// Logger handles logging of security events
// CWE-778: Ensures sufficient logging of security events for audit and forensics
type Logger interface {
	// LogEvent logs a security event
	LogEvent(ctx context.Context, event *securityevent.SecurityEvent) error

	// LogAccountLocked logs an account lockout event
	LogAccountLocked(ctx context.Context, emailHash, clientIP string, failedAttempts int, lockoutDuration string) error

	// LogAccountUnlocked logs an account unlock event
	LogAccountUnlocked(ctx context.Context, emailHash, unlockedBy string) error

	// LogFailedLogin logs a failed login attempt
	LogFailedLogin(ctx context.Context, emailHash, clientIP string, remainingAttempts int) error

	// LogExcessiveFailedLogin logs excessive failed login attempts
	LogExcessiveFailedLogin(ctx context.Context, emailHash, clientIP string, attemptCount int) error

	// LogSuccessfulLogin logs a successful login
	LogSuccessfulLogin(ctx context.Context, emailHash, clientIP string) error

	// LogIPRateLimitExceeded logs IP rate limit exceeded
	LogIPRateLimitExceeded(ctx context.Context, clientIP string) error
}

type securityEventLogger struct {
	logger *zap.Logger
}

// NewSecurityEventLogger creates a new security event logger
func NewSecurityEventLogger(logger *zap.Logger) Logger {
	return &securityEventLogger{
		logger: logger.Named("security-events"),
	}
}

// ProvideSecurityEventLogger provides a SecurityEventLogger for dependency injection
func ProvideSecurityEventLogger(logger *zap.Logger) Logger {
	return NewSecurityEventLogger(logger)
}

// LogEvent logs a security event
func (s *securityEventLogger) LogEvent(ctx context.Context, event *securityevent.SecurityEvent) error {
	// Map severity to log level
	logFunc := s.logger.Info
	switch event.Severity {
	case securityevent.SeverityLow:
		logFunc = s.logger.Info
	case securityevent.SeverityMedium:
		logFunc = s.logger.Warn
	case securityevent.SeverityHigh, securityevent.SeverityCritical:
		logFunc = s.logger.Error
	}

	// Build log fields
	fields := []zap.Field{
		zap.String("event_id", event.ID),
		zap.String("event_type", string(event.EventType)),
		zap.String("severity", string(event.Severity)),
		zap.String("email_hash", event.EmailHash),
		zap.String("client_ip", event.ClientIP),
		zap.Time("timestamp", event.Timestamp),
	}

	if event.UserAgent != "" {
		fields = append(fields, zap.String("user_agent", event.UserAgent))
	}

	// Add metadata fields
	for key, value := range event.Metadata {
		fields = append(fields, zap.Any(key, value))
	}

	logFunc(event.Message, fields...)

	// TODO: In production, also persist to a security event database/SIEM
	// This could be implemented as a repository pattern:
	// - Store in Cassandra for long-term retention
	// - Send to SIEM (Splunk, ELK, etc.) for analysis
	// - Send to monitoring/alerting system

	return nil
}

// LogAccountLocked logs an account lockout event
func (s *securityEventLogger) LogAccountLocked(ctx context.Context, emailHash, clientIP string, failedAttempts int, lockoutDuration string) error {
	event := securityevent.NewSecurityEvent(
		securityevent.EventTypeAccountLocked,
		securityevent.SeverityHigh,
		emailHash,
		clientIP,
		"Account locked due to excessive failed login attempts",
	)
	event.WithMetadata("failed_attempts", failedAttempts)
	event.WithMetadata("lockout_duration", lockoutDuration)

	return s.LogEvent(ctx, event)
}

// LogAccountUnlocked logs an account unlock event
func (s *securityEventLogger) LogAccountUnlocked(ctx context.Context, emailHash, unlockedBy string) error {
	event := securityevent.NewSecurityEvent(
		securityevent.EventTypeAccountUnlocked,
		securityevent.SeverityMedium,
		emailHash,
		"",
		"Account manually unlocked by administrator",
	)
	event.WithMetadata("unlocked_by", unlockedBy)

	return s.LogEvent(ctx, event)
}

// LogFailedLogin logs a failed login attempt
func (s *securityEventLogger) LogFailedLogin(ctx context.Context, emailHash, clientIP string, remainingAttempts int) error {
	event := securityevent.NewSecurityEvent(
		securityevent.EventTypeFailedLogin,
		securityevent.SeverityMedium,
		emailHash,
		clientIP,
		"Failed login attempt - invalid credentials",
	)
	event.WithMetadata("remaining_attempts", remainingAttempts)

	return s.LogEvent(ctx, event)
}

// LogExcessiveFailedLogin logs excessive failed login attempts
func (s *securityEventLogger) LogExcessiveFailedLogin(ctx context.Context, emailHash, clientIP string, attemptCount int) error {
	event := securityevent.NewSecurityEvent(
		securityevent.EventTypeExcessiveFailedLogin,
		securityevent.SeverityHigh,
		emailHash,
		clientIP,
		"Excessive failed login attempts detected",
	)
	event.WithMetadata("attempt_count", attemptCount)

	return s.LogEvent(ctx, event)
}

// LogSuccessfulLogin logs a successful login
func (s *securityEventLogger) LogSuccessfulLogin(ctx context.Context, emailHash, clientIP string) error {
	event := securityevent.NewSecurityEvent(
		securityevent.EventTypeSuccessfulLogin,
		securityevent.SeverityLow,
		emailHash,
		clientIP,
		"Successful login",
	)

	return s.LogEvent(ctx, event)
}

// LogIPRateLimitExceeded logs IP rate limit exceeded
func (s *securityEventLogger) LogIPRateLimitExceeded(ctx context.Context, clientIP string) error {
	event := securityevent.NewSecurityEvent(
		securityevent.EventTypeIPRateLimitExceeded,
		securityevent.SeverityMedium,
		"",
		clientIP,
		"IP rate limit exceeded for login attempts",
	)

	return s.LogEvent(ctx, event)
}
