package handlers

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgxpool"
	"ticketing-backend/internal/config"
	"ticketing-backend/internal/models"
	"ticketing-backend/internal/services"
	"ticketing-backend/internal/utils"
)

type AuthHandler struct {
	pool        *pgxpool.Pool
	authService *services.AuthService
	jwtSecret   string
	settings    *config.AppSettings
}

func NewAuthHandler(pool *pgxpool.Pool, authService *services.AuthService, jwtSecret string, settings *config.AppSettings) *AuthHandler {
	return &AuthHandler{pool: pool, authService: authService, jwtSecret: jwtSecret, settings: settings}
}

func (h *AuthHandler) Login(c *gin.Context) {
	var req models.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "Missing email or password")
		return
	}

	// Get user
	var user models.User
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT id, email, password_hash, full_name, role, is_verified FROM users WHERE email = $1`,
		req.Email,
	).Scan(&user.ID, &user.Email, &user.PasswordHash, &user.FullName, &user.Role, &user.IsVerified)

	if err != nil {
		utils.Error(c, http.StatusUnauthorized, "Invalid credentials")
		return
	}

	if user.PasswordHash == nil {
		utils.Forbidden(c, "Akun ini tidak memiliki password. Silakan masuk dengan Google SSO.")
		return
	}

	if !h.authService.VerifyPassword(req.Password, *user.PasswordHash) {
		utils.Error(c, http.StatusUnauthorized, "Invalid credentials")
		return
	}

	if !user.IsVerified {
		utils.Forbidden(c, "Please verify your email before logging in")
		return
	}

	tokens, err := h.authService.GenerateTokens(user.ID, user.Email, string(user.Role))
	if err != nil {
		utils.InternalError(c, "Login failed")
		return
	}

	loginTarget := ""
	if req.LoginTarget != nil {
		loginTarget = *req.LoginTarget
	}

	isDashboardLogin := loginTarget == "dashboard"

	visitorRedirect := h.settings.GetVisitorRedirectURL()

	var redirectPath string
	if isDashboardLogin || user.Role == "super_admin" {
		redirectPath = h.authService.GetDashboardPath(string(user.Role))
	} else {
		redirectPath = visitorRedirect
	}

	// Log auth action
	ipAddress := c.ClientIP()
	h.authService.LogAuthAction(user.ID, "login_success", "success", ipAddress)

	utils.Success(c, gin.H{
		"user": gin.H{
			"id":       user.ID,
			"email":    user.Email,
			"fullName": user.FullName,
			"role":     user.Role,
		},
		"tokens":       tokens,
		"redirectPath": redirectPath,
	})
}

func (h *AuthHandler) Register(c *gin.Context) {
	var req models.RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "Invalid registration data: "+err.Error())
		return
	}

	// Check if registration is enabled (file-based config)
	if !h.settings.IsRegistrationEnabled() {
		utils.Forbidden(c, "Registration is currently disabled by admin")
		return
	}

	// Check if email already exists
	var exists bool
	h.pool.QueryRow(c.Request.Context(),
		`SELECT EXISTS(SELECT 1 FROM users WHERE email = $1)`, req.Email,
	).Scan(&exists)
	if exists {
		utils.BadRequest(c, "Email sudah terdaftar")
		return
	}

	passwordHash, err := h.authService.HashPassword(req.Password)
	if err != nil {
		utils.InternalError(c, "Failed to process password")
		return
	}

	var user models.User
	err = h.pool.QueryRow(c.Request.Context(),
		`INSERT INTO users (email, password_hash, full_name, role, is_verified)
		 VALUES ($1, $2, $3, 'visitor', FALSE)
		 RETURNING id, email, full_name, role, is_verified, created_at`,
		req.Email, passwordHash, req.FullName,
	).Scan(&user.ID, &user.Email, &user.FullName, &user.Role, &user.IsVerified, &user.CreatedAt)

	if err != nil {
		utils.InternalError(c, "Gagal mendaftarkan akun")
		return
	}

	// Create email verification token
	token := uuid.New().String()
	_, _ = h.pool.Exec(c.Request.Context(),
		`INSERT INTO email_verification_tokens (user_id, token, expires_at)
		 VALUES ($1, $2, $3)`,
		user.ID, token, time.Now().Add(24*time.Hour),
	)

	utils.Created(c, gin.H{
		"user": gin.H{
			"id":       user.ID,
			"email":    user.Email,
			"fullName": user.FullName,
			"role":     user.Role,
		},
		"message": "Registration successful. Please check your email to verify your account.",
	})
}

func (h *AuthHandler) Me(c *gin.Context) {
	userID, _ := c.Get("userId")

	var user models.User
	var className *string
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT u.id, u.email, u.full_name, u.role, u.is_verified, u.class_id, c.name as class_name
		 FROM users u
		 LEFT JOIN classes c ON c.id = u.class_id
		 WHERE u.id = $1`, userID,
	).Scan(&user.ID, &user.Email, &user.FullName, &user.Role, &user.IsVerified, &user.ClassID, &className)
	if err != nil {
		utils.NotFound(c, "User not found")
		return
	}

	utils.Success(c, gin.H{
		"user": gin.H{
			"id":         user.ID,
			"email":      user.Email,
			"fullName":   user.FullName,
			"role":       user.Role,
			"isVerified": user.IsVerified,
			"classId":    user.ClassID,
			"className":  className,
		},
	})
}

func (h *AuthHandler) Logout(c *gin.Context) {
	refreshToken, _ := c.Cookie("refreshToken")
	if refreshToken != "" {
		h.authService.RevokeRefreshToken(refreshToken)
	}

	utils.Success(c, gin.H{"message": "Logged out successfully"})
}

func (h *AuthHandler) Refresh(c *gin.Context) {
	var req struct {
		RefreshToken string `json:"refreshToken"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		// Try cookie
		req.RefreshToken, _ = c.Cookie("refreshToken")
	}

	if req.RefreshToken == "" {
		utils.BadRequest(c, "Refresh token is required")
		return
	}

	tokens, err := h.authService.RefreshAccessToken(req.RefreshToken)
	if err != nil {
		utils.Error(c, http.StatusUnauthorized, "Invalid refresh token")
		return
	}

	utils.Success(c, gin.H{"tokens": tokens})
}

func (h *AuthHandler) LoginMethods(c *gin.Context) {
	methods := gin.H{
		"passwordEnabled":      h.settings.IsLoginPasswordEnabled(),
		"googleEnabled":        h.settings.IsLoginGoogleEnabled(),
		"googleRegisteredOnly": h.settings.IsGoogleRegisteredOnly(),
		"registrationEnabled":  h.settings.IsRegistrationEnabled(),
	}
	utils.Success(c, gin.H{"methods": methods})
}

func (h *AuthHandler) ForgotPassword(c *gin.Context) {
	var req struct {
		Email string `json:"email" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "Email is required")
		return
	}

	var userID string
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT id FROM users WHERE email = $1`, req.Email,
	).Scan(&userID)

	if err != nil {
		// Don't reveal if email exists
		utils.Success(c, gin.H{"message": "If the email exists, a reset link will be sent"})
		return
	}

	resetToken := uuid.New().String()
	_, _ = h.pool.Exec(c.Request.Context(),
		`UPDATE users SET password_reset_token = $1, password_reset_expires = $2 WHERE id = $3`,
		resetToken, time.Now().Add(1*time.Hour), userID,
	)

	utils.Success(c, gin.H{"message": "If the email exists, a reset link will be sent"})
}

func (h *AuthHandler) ResetPassword(c *gin.Context) {
	var req struct {
		Token       string `json:"token" binding:"required"`
		NewPassword string `json:"newPassword" binding:"required,min=8"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "Invalid request")
		return
	}

	var userID string
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT id FROM users
		 WHERE password_reset_token = $1 AND password_reset_expires > NOW()`,
		req.Token,
	).Scan(&userID)

	if err != nil {
		utils.BadRequest(c, "Invalid or expired reset token")
		return
	}

	passwordHash, err := h.authService.HashPassword(req.NewPassword)
	if err != nil {
		utils.InternalError(c, "Failed to process password")
		return
	}

	_, _ = h.pool.Exec(c.Request.Context(),
		`UPDATE users SET password_hash = $1, password_reset_token = NULL, password_reset_expires = NULL WHERE id = $2`,
		passwordHash, userID,
	)

	utils.Success(c, gin.H{"message": "Password reset successful"})
}

func (h *AuthHandler) VerifyEmail(c *gin.Context) {
	token := c.Query("token")
	if token == "" {
		utils.BadRequest(c, "Verification token is required")
		return
	}

	var userID string
	err := h.pool.QueryRow(c.Request.Context(),
		`SELECT user_id FROM email_verification_tokens
		 WHERE token = $1 AND expires_at > NOW()`, token,
	).Scan(&userID)

	if err != nil {
		utils.BadRequest(c, "Invalid or expired verification token")
		return
	}

	_, _ = h.pool.Exec(c.Request.Context(),
		`UPDATE users SET is_verified = TRUE, email_verified_at = NOW() WHERE id = $1`, userID,
	)

	_, _ = h.pool.Exec(c.Request.Context(),
		`DELETE FROM email_verification_tokens WHERE token = $1`, token,
	)

	utils.Success(c, gin.H{"message": "Email verified successfully"})
}

func (h *AuthHandler) GoogleAuth(c *gin.Context) {
	utils.Success(c, gin.H{"message": "Google auth endpoint"})
}

func (h *AuthHandler) GoogleCallback(c *gin.Context) {
	utils.Success(c, gin.H{"message": "Google callback endpoint"})
}

func (h *AuthHandler) DashboardLogin(c *gin.Context) {
	var req models.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		utils.BadRequest(c, "Missing email or password")
		return
	}

	loginTarget := "dashboard"
	req.LoginTarget = &loginTarget
	h.Login(c)
}
