package handlers

import (
	"context"
	"net/http"

	"github.com/breakpilot/consent-service/internal/middleware"
	"github.com/breakpilot/consent-service/internal/models"
	"github.com/breakpilot/consent-service/internal/services"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// ========================================
// 2FA (TOTP) Endpoints
// ========================================

// Setup2FA initiates 2FA setup
// POST /auth/2fa/setup
func (h *OAuthHandler) Setup2FA(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	// Get user email
	ctx := context.Background()
	user, err := h.authService.GetUserByID(ctx, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user"})
		return
	}

	// Setup 2FA
	response, err := h.totpService.Setup2FA(ctx, userID, user.Email)
	if err != nil {
		switch err {
		case services.ErrTOTPAlreadyEnabled:
			c.JSON(http.StatusConflict, gin.H{"error": "2FA is already enabled for this account"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to setup 2FA"})
		}
		return
	}

	c.JSON(http.StatusOK, response)
}

// Verify2FASetup verifies the 2FA setup with a code
// POST /auth/2fa/verify-setup
func (h *OAuthHandler) Verify2FASetup(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	var req models.Verify2FARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	ctx := context.Background()
	err = h.totpService.Verify2FASetup(ctx, userID, req.Code)
	if err != nil {
		switch err {
		case services.ErrTOTPAlreadyEnabled:
			c.JSON(http.StatusConflict, gin.H{"error": "2FA is already enabled"})
		case services.ErrTOTPInvalidCode:
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to verify 2FA setup"})
		}
		return
	}

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

// Verify2FAChallenge verifies a 2FA challenge during login
// POST /auth/2fa/verify
func (h *OAuthHandler) Verify2FAChallenge(c *gin.Context) {
	var req models.Verify2FAChallengeRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	ctx := context.Background()
	var userID *uuid.UUID
	var err error

	if req.RecoveryCode != "" {
		// Verify with recovery code
		userID, err = h.totpService.VerifyChallengeWithRecoveryCode(ctx, req.ChallengeID, req.RecoveryCode)
	} else {
		// Verify with TOTP code
		userID, err = h.totpService.VerifyChallenge(ctx, req.ChallengeID, req.Code)
	}

	if err != nil {
		switch err {
		case services.ErrTOTPChallengeExpired:
			c.JSON(http.StatusGone, gin.H{"error": "2FA challenge has expired"})
		case services.ErrTOTPInvalidCode:
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
		case services.ErrRecoveryCodeInvalid:
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid recovery code"})
		default:
			c.JSON(http.StatusBadRequest, gin.H{"error": "2FA verification failed"})
		}
		return
	}

	// Get user and generate tokens
	user, err := h.authService.GetUserByID(ctx, *userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user"})
		return
	}

	// Generate access token
	accessToken, err := h.authService.GenerateAccessToken(user)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
		return
	}

	// Generate refresh token
	refreshToken, refreshTokenHash, err := h.authService.GenerateRefreshToken()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate refresh token"})
		return
	}

	// Store session
	ipAddress := middleware.GetClientIP(c)
	userAgent := middleware.GetUserAgent(c)

	// We need direct DB access for this, or we need to add a method to AuthService
	// For now, we'll return the tokens and let the caller handle session storage
	c.JSON(http.StatusOK, gin.H{
		"access_token":  accessToken,
		"refresh_token": refreshToken,
		"token_type":    "Bearer",
		"expires_in":    3600,
		"user": map[string]interface{}{
			"id":    user.ID,
			"email": user.Email,
			"name":  user.Name,
			"role":  user.Role,
		},
		"_session_hash": refreshTokenHash,
		"_ip":           ipAddress,
		"_user_agent":   userAgent,
	})
}

// Disable2FA disables 2FA for the current user
// POST /auth/2fa/disable
func (h *OAuthHandler) Disable2FA(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	var req models.Verify2FARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	ctx := context.Background()
	err = h.totpService.Disable2FA(ctx, userID, req.Code)
	if err != nil {
		switch err {
		case services.ErrTOTPNotEnabled:
			c.JSON(http.StatusNotFound, gin.H{"error": "2FA is not enabled"})
		case services.ErrTOTPInvalidCode:
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to disable 2FA"})
		}
		return
	}

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

// Get2FAStatus returns the 2FA status for the current user
// GET /auth/2fa/status
func (h *OAuthHandler) Get2FAStatus(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	ctx := context.Background()
	status, err := h.totpService.GetStatus(ctx, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get 2FA status"})
		return
	}

	c.JSON(http.StatusOK, status)
}

// RegenerateRecoveryCodes generates new recovery codes
// POST /auth/2fa/recovery-codes
func (h *OAuthHandler) RegenerateRecoveryCodes(c *gin.Context) {
	userID, err := middleware.GetUserID(c)
	if err != nil || userID == uuid.Nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	var req models.Verify2FARequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	ctx := context.Background()
	codes, err := h.totpService.RegenerateRecoveryCodes(ctx, userID, req.Code)
	if err != nil {
		switch err {
		case services.ErrTOTPNotEnabled:
			c.JSON(http.StatusNotFound, gin.H{"error": "2FA is not enabled"})
		case services.ErrTOTPInvalidCode:
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid 2FA code"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to regenerate recovery codes"})
		}
		return
	}

	c.JSON(http.StatusOK, gin.H{"recovery_codes": codes})
}

// ========================================
// Enhanced Login with 2FA
// ========================================

// LoginWith2FA handles login with optional 2FA
// POST /auth/login
func (h *OAuthHandler) LoginWith2FA(c *gin.Context) {
	var req models.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	ctx := context.Background()
	ipAddress := middleware.GetClientIP(c)
	userAgent := middleware.GetUserAgent(c)

	// Attempt login
	response, err := h.authService.Login(ctx, &req, ipAddress, userAgent)
	if err != nil {
		switch err {
		case services.ErrInvalidCredentials:
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
		case services.ErrAccountLocked:
			c.JSON(http.StatusForbidden, gin.H{"error": "Account is temporarily locked"})
		case services.ErrAccountSuspended:
			c.JSON(http.StatusForbidden, gin.H{"error": "Account is suspended"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Login failed"})
		}
		return
	}

	// Check if 2FA is enabled
	twoFactorEnabled, _ := h.totpService.IsTwoFactorEnabled(ctx, response.User.ID)

	if twoFactorEnabled {
		// Create 2FA challenge
		challengeID, err := h.totpService.CreateChallenge(ctx, response.User.ID, ipAddress, userAgent)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create 2FA challenge"})
			return
		}

		// Return 2FA required response
		c.JSON(http.StatusOK, gin.H{
			"requires_2fa": true,
			"challenge_id": challengeID,
			"message":      "2FA verification required",
		})
		return
	}

	// No 2FA required, return tokens
	c.JSON(http.StatusOK, gin.H{
		"requires_2fa":  false,
		"access_token":  response.AccessToken,
		"refresh_token": response.RefreshToken,
		"token_type":    "Bearer",
		"expires_in":    response.ExpiresIn,
		"user": map[string]interface{}{
			"id":    response.User.ID,
			"email": response.User.Email,
			"name":  response.User.Name,
			"role":  response.User.Role,
		},
	})
}

// ========================================
// Registration with mandatory 2FA setup
// ========================================

// RegisterWith2FA handles registration with mandatory 2FA setup
// POST /auth/register
func (h *OAuthHandler) RegisterWith2FA(c *gin.Context) {
	var req models.RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
		return
	}

	ctx := context.Background()

	// Validate password strength
	if len(req.Password) < 8 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Password must be at least 8 characters"})
		return
	}

	// Register user
	user, verificationToken, err := h.authService.Register(ctx, &req)
	if err != nil {
		switch err {
		case services.ErrUserExists:
			c.JSON(http.StatusConflict, gin.H{"error": "A user with this email already exists"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Registration failed"})
		}
		return
	}

	// Setup 2FA immediately
	twoFAResponse, err := h.totpService.Setup2FA(ctx, user.ID, user.Email)
	if err != nil {
		// Non-fatal - user can set up 2FA later, but log it
		c.JSON(http.StatusCreated, gin.H{
			"message":            "Registration successful. Please verify your email.",
			"user_id":            user.ID,
			"verification_token": verificationToken, // In production, this would be sent via email
			"two_factor_setup":   nil,
			"two_factor_error":   "Failed to initialize 2FA. Please set it up in your account settings.",
		})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"message":            "Registration successful. Please verify your email and complete 2FA setup.",
		"user_id":            user.ID,
		"verification_token": verificationToken, // In production, this would be sent via email
		"two_factor_setup": map[string]interface{}{
			"secret":         twoFAResponse.Secret,
			"qr_code":        twoFAResponse.QRCodeDataURL,
			"recovery_codes": twoFAResponse.RecoveryCodes,
			"setup_required": true,
			"setup_endpoint": "/auth/2fa/verify-setup",
		},
	})
}
