// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/auth/verify_email.go
package auth

import (
	"context"
	"fmt"
	"strings"
	"time"

	"go.uber.org/zap"

	uc_user "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/user"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/auditlog"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/validation"
)

type VerifyEmailRequestDTO struct {
	Code string `json:"code"`
}

type VerifyEmailResponseDTO struct {
	Message  string `json:"message"`
	Success  bool   `json:"success"`
	UserRole int8   `json:"user_role"`
}

type VerifyEmailService interface {
	Execute(ctx context.Context, req *VerifyEmailRequestDTO) (*VerifyEmailResponseDTO, error)
}

type verifyEmailServiceImpl struct {
	logger                        *zap.Logger
	auditLogger                   auditlog.AuditLogger
	userGetByVerificationCodeUC   uc_user.UserGetByVerificationCodeUseCase
	userUpdateUC                  uc_user.UserUpdateUseCase
}

func NewVerifyEmailService(
	logger *zap.Logger,
	auditLogger auditlog.AuditLogger,
	userGetByVerificationCodeUC uc_user.UserGetByVerificationCodeUseCase,
	userUpdateUC uc_user.UserUpdateUseCase,
) VerifyEmailService {
	return &verifyEmailServiceImpl{
		logger:                        logger.Named("VerifyEmailService"),
		auditLogger:                   auditLogger,
		userGetByVerificationCodeUC:   userGetByVerificationCodeUC,
		userUpdateUC:                  userUpdateUC,
	}
}

func (s *verifyEmailServiceImpl) Execute(ctx context.Context, req *VerifyEmailRequestDTO) (*VerifyEmailResponseDTO, error) {
	// Validate request
	if err := s.validateVerifyEmailRequest(req); err != nil {
		return nil, err // Returns RFC 9457 ProblemDetail
	}

	// Get user by verification code
	user, err := s.userGetByVerificationCodeUC.Execute(ctx, req.Code)
	if err != nil || user == nil {
		s.logger.Warn("Invalid verification code attempted")
		return nil, httperror.NewNotFoundError("Verification code not found or has already been used")
	}

	// Check if code has expired
	if time.Now().After(user.SecurityData.CodeExpiry) {
		s.logger.Warn("Verification code expired",
			zap.String("user_id", user.ID.String()),
			zap.Time("expiry", user.SecurityData.CodeExpiry))
		return nil, httperror.NewBadRequestError("Verification code has expired. Please request a new verification email.")
	}

	// Update user to mark as verified
	user.SecurityData.WasEmailVerified = true
	user.SecurityData.Code = ""
	user.SecurityData.CodeExpiry = time.Time{}
	user.ModifiedAt = time.Now()

	if err := s.userUpdateUC.Execute(ctx, user); err != nil {
		s.logger.Error("Failed to update user", zap.Error(err))
		return nil, httperror.NewInternalServerError(fmt.Sprintf("Failed to verify email: %v", err))
	}

	s.logger.Info("Email verified successfully", zap.String("user_id", user.ID.String()))

	// Audit log email verification
	s.auditLogger.LogAuth(ctx, auditlog.EventTypeEmailVerified, auditlog.OutcomeSuccess,
		validation.MaskEmail(user.Email), "", map[string]string{
			"user_id": user.ID.String(),
		})

	return &VerifyEmailResponseDTO{
		Message:  "Email verified successfully. You can now log in.",
		Success:  true,
		UserRole: user.Role,
	}, nil
}

// validateVerifyEmailRequest validates the verify email request.
// Returns RFC 9457 ProblemDetail error with field-specific errors.
func (s *verifyEmailServiceImpl) validateVerifyEmailRequest(req *VerifyEmailRequestDTO) error {
	errors := make(map[string]string)

	// Validate verification code
	code := strings.TrimSpace(req.Code)
	if code == "" {
		errors["code"] = "Verification code is required"
	} else if len(code) != 8 {
		errors["code"] = "Verification code must be 8 digits"
	} else {
		// Validate that code is numeric
		for _, c := range code {
			if c < '0' || c > '9' {
				errors["code"] = "Verification code must contain only numbers"
				break
			}
		}
	}

	// If there are validation errors, return RFC 9457 error
	if len(errors) > 0 {
		return httperror.NewValidationError(errors)
	}

	return nil
}
