package handlers

import (
	"context"
	"net/http"

	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"

	apperrors "veza-backend-api/internal/errors"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// TwoFactorServiceInterface defines methods needed for 2FA handler
type TwoFactorServiceInterface interface {
	GetTwoFactorStatus(ctx context.Context, userID uuid.UUID) (bool, error)
	GenerateSecret(user *models.User) (*services.TwoFactorSetup, error)
	VerifyTOTPCode(secret, code string) bool
	GenerateRecoveryCodes() []string
	EnableTwoFactor(ctx context.Context, userID uuid.UUID, secret string, recoveryCodes []string) error
	DisableTwoFactor(ctx context.Context, userID uuid.UUID) error
}

// UserServiceInterface defines methods needed for user operations
type UserServiceInterface interface {
	GetByID(userID uuid.UUID) (*models.User, error)
}

// TwoFactorHandler handles 2FA-related API endpoints
// BE-API-001: Implement 2FA endpoints (setup, verify, disable)
type TwoFactorHandler struct {
	twoFactorService TwoFactorServiceInterface
	userService      UserServiceInterface
	logger           *zap.Logger
}

// NewTwoFactorHandler creates new 2FA handler
func NewTwoFactorHandler(twoFactorService *services.TwoFactorService, userService *services.UserService, logger *zap.Logger) *TwoFactorHandler {
	return &TwoFactorHandler{
		twoFactorService: twoFactorService,
		userService:      userService,
		logger:           logger,
	}
}

// NewTwoFactorHandlerWithInterface creates new 2FA handler with interfaces (for testing)
func NewTwoFactorHandlerWithInterface(twoFactorService TwoFactorServiceInterface, userService UserServiceInterface, logger *zap.Logger) *TwoFactorHandler {
	return &TwoFactorHandler{
		twoFactorService: twoFactorService,
		userService:      userService,
		logger:           logger,
	}
}

// SetupTwoFactorRequest represents the request for 2FA setup
type SetupTwoFactorRequest struct {
	// No fields needed - user is authenticated
}

// SetupTwoFactorResponse represents the response for 2FA setup
type SetupTwoFactorResponse struct {
	Secret        string   `json:"secret"`
	QRCodeURL     string   `json:"qr_code_url"`
	RecoveryCodes []string `json:"recovery_codes"`
}

// SetupTwoFactor initiates 2FA setup for a user
// @Summary      Setup 2FA
// @Description  Generate 2FA secret and QR code for setup
// @Tags         Auth
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200  {object}  APIResponse{data=SetupTwoFactorResponse}
// @Failure      400  {object}  APIResponse "2FA already enabled"
// @Failure      401  {object}  APIResponse "Unauthorized"
// @Failure      500  {object}  APIResponse "Internal Error"
// @Router       /auth/2fa/setup [post]
func (h *TwoFactorHandler) SetupTwoFactor(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Error already sent by GetUserIDUUID
	}

	// Check if 2FA is already enabled
	enabled, err := h.twoFactorService.GetTwoFactorStatus(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to get 2FA status", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get 2FA status", err))
		return
	}

	if enabled {
		RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "2FA is already enabled"))
		return
	}

	// Get user information
	user, err := h.userService.GetByID(userID)
	if err != nil {
		h.logger.Error("Failed to get user", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get user information", err))
		return
	}

	// Generate 2FA setup
	setup, err := h.twoFactorService.GenerateSecret(user)
	if err != nil {
		h.logger.Error("Failed to generate 2FA setup", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to generate 2FA setup", err))
		return
	}

	RespondSuccess(c, http.StatusOK, SetupTwoFactorResponse{
		Secret:        setup.Secret,
		QRCodeURL:     setup.QRCodeURL,
		RecoveryCodes: setup.RecoveryCodes,
	})
}

// VerifyTwoFactorRequest represents the request for 2FA verification
type VerifyTwoFactorRequest struct {
	Secret string `json:"secret" binding:"required"` // Secret from setup step
	Code   string `json:"code" binding:"required"`   // TOTP code to verify
}

// VerifyTwoFactor verifies a 2FA code and enables 2FA
// @Summary      Verify and Enable 2FA
// @Description  Verify 2FA code and enable 2FA for user
// @Tags         Auth
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        request body VerifyTwoFactorRequest true "2FA Code"
// @Success      200  {object}  APIResponse{data=object{message=string}}
// @Failure      400  {object}  APIResponse "Invalid code"
// @Failure      401  {object}  APIResponse "Unauthorized"
// @Failure      500  {object}  APIResponse "Internal Error"
// @Router       /auth/2fa/verify [post]
func (h *TwoFactorHandler) VerifyTwoFactor(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Error already sent by GetUserIDUUID
	}

	var req VerifyTwoFactorRequest
	commonHandler := NewCommonHandler(h.logger)
	if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Check if 2FA is already enabled
	enabled, err := h.twoFactorService.GetTwoFactorStatus(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to get 2FA status", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get 2FA status", err))
		return
	}

	if enabled {
		RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "2FA is already enabled"))
		return
	}

	// Verify the code with the secret provided from setup step
	valid := h.twoFactorService.VerifyTOTPCode(req.Secret, req.Code)
	if !valid {
		RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "Invalid 2FA code"))
		return
	}

	// Generate recovery codes for this secret
	recoveryCodes := h.twoFactorService.GenerateRecoveryCodes()

	// Enable 2FA with the verified secret
	err = h.twoFactorService.EnableTwoFactor(c.Request.Context(), userID, req.Secret, recoveryCodes)
	if err != nil {
		h.logger.Error("Failed to enable 2FA", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to enable 2FA", err))
		return
	}

	RespondSuccess(c, http.StatusOK, gin.H{"message": "2FA enabled successfully"})
}

// DisableTwoFactorRequest represents the request for disabling 2FA
type DisableTwoFactorRequest struct {
	Password string `json:"password" binding:"required"`
}

// DisableTwoFactor disables 2FA for a user (requires password confirmation)
// @Summary      Disable 2FA
// @Description  Disable 2FA for user (requires password confirmation)
// @Tags         Auth
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        request body DisableTwoFactorRequest true "Password Confirmation"
// @Success      200  {object}  APIResponse{data=object{message=string}}
// @Failure      400  {object}  APIResponse "Invalid password or 2FA not enabled"
// @Failure      401  {object}  APIResponse "Unauthorized"
// @Failure      500  {object}  APIResponse "Internal Error"
// @Router       /auth/2fa/disable [post]
func (h *TwoFactorHandler) DisableTwoFactor(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Error already sent by GetUserIDUUID
	}

	var req DisableTwoFactorRequest
	commonHandler := NewCommonHandler(h.logger)
	if appErr := commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
		RespondWithAppError(c, appErr)
		return
	}

	// Check if 2FA is enabled
	enabled, err := h.twoFactorService.GetTwoFactorStatus(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to get 2FA status", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get 2FA status", err))
		return
	}

	if !enabled {
		RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "2FA is not enabled"))
		return
	}

	// TODO: Verify password using password service
	// For now, we'll skip password verification in MVP
	// In production, verify password before disabling 2FA
	// Password verification would be:
	// user, err := h.userService.GetByID(userID)
	// if err != nil { ... }
	// if !passwordService.VerifyPassword(user.PasswordHash, req.Password) { ... }

	// Disable 2FA
	err = h.twoFactorService.DisableTwoFactor(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to disable 2FA", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to disable 2FA", err))
		return
	}

	RespondSuccess(c, http.StatusOK, gin.H{"message": "2FA disabled successfully"})
}

// GetTwoFactorStatus gets the 2FA status for a user
// @Summary      Get 2FA Status
// @Description  Get 2FA enabled status for authenticated user
// @Tags         Auth
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200  {object}  APIResponse{data=object{enabled=bool}}
// @Failure      401  {object}  APIResponse "Unauthorized"
// @Failure      500  {object}  APIResponse "Internal Error"
// @Router       /auth/2fa/status [get]
func (h *TwoFactorHandler) GetTwoFactorStatus(c *gin.Context) {
	userID, ok := GetUserIDUUID(c)
	if !ok {
		return // Error already sent by GetUserIDUUID
	}

	enabled, err := h.twoFactorService.GetTwoFactorStatus(c.Request.Context(), userID)
	if err != nil {
		h.logger.Error("Failed to get 2FA status", zap.Error(err), zap.String("user_id", userID.String()))
		RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get 2FA status", err))
		return
	}

	RespondSuccess(c, http.StatusOK, gin.H{"enabled": enabled})
}
