package handlers

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"gauth-central/internal/database"
	"gauth-central/internal/models"
	"gauth-central/pkg/utils"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"
)

type ProfileHandler struct{}

func NewProfileHandler() *ProfileHandler {
	return &ProfileHandler{}
}

// isOAuthUser checks if user is an OAuth user (has social accounts)
func isOAuthUser(user *models.User) bool {
	// OAuth user if they have social accounts OR if they don't have a password
	return len(user.SocialAccounts) > 0 || user.Password == ""
}

// GetProfile godoc
// @Summary Get current user profile
// @Tags Profile
// @Security ApiKeyAuth
// @Produce json
// @Success 200 {object} models.User
// @Router /profile [get]
func (h *ProfileHandler) GetProfile(c *gin.Context) {
	userID := c.GetString("user_id")
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
		return
	}

	var user models.User
	if err := database.DB.
		Preload("Roles").
		Preload("Roles.Permissions").
		Preload("SocialAccounts").
		Where("id = ?", userID).
		First(&user).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}

	// Add is_oauth_user flag to response
	type ProfileResponse struct {
		models.User
		IsOAuthUser bool `json:"is_oauth_user"`
	}

	response := ProfileResponse{
		User:        user,
		IsOAuthUser: isOAuthUser(&user),
	}

	c.JSON(http.StatusOK, response)
}

// UpdateProfile godoc
// @Summary Update current user profile
// @Tags Profile
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param user_name formData string false "Username"
// @Param avatar formData file false "Avatar image"
// @Success 200 {object} map[string]interface{}
// @Router /profile [put]
func (h *ProfileHandler) UpdateProfile(c *gin.Context) {
	userID := c.GetString("user_id")
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
		return
	}

	// Try to parse as multipart form first
	contentType := c.GetHeader("Content-Type")
	isMultipart := strings.Contains(contentType, "multipart/form-data")

	updates := make(map[string]interface{})

	if isMultipart {
		// Parse multipart form
		if err := c.Request.ParseMultipartForm(32 << 20); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to parse form data"})
			return
		}

		// Get form values
		if userName := c.PostForm("user_name"); userName != "" {
			updates["user_name"] = userName
		}
	} else {
		// Parse as JSON
		var input struct {
			UserName *string `json:"user_name"`
		}

		if err := c.ShouldBindJSON(&input); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if input.UserName != nil {
			updates["user_name"] = *input.UserName
		}
	}

	// Update basic user fields
	if len(updates) > 0 {
		if err := database.DB.Model(&models.User{}).Where("id = ?", userID).Updates(updates).Error; err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update profile"})
			return
		}
	}

	// Handle avatar upload if multipart and file provided
	if isMultipart {
		avatarFile, err := c.FormFile("avatar")
		if err == nil && avatarFile != nil {
			// Validate file size (max 5MB)
			if avatarFile.Size > 5*1024*1024 {
				c.JSON(http.StatusBadRequest, gin.H{"error": "Avatar file size exceeds 5MB limit"})
				return
			}

			// Get user to check for old avatar
			var user models.User
			if err := database.DB.Where("id = ?", userID).First(&user).Error; err != nil {
				c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
				return
			}

			// Delete old avatar if exists and is local
			if user.Avatar != "" && strings.HasPrefix(user.Avatar, "/uploads/") {
				oldPath := "." + user.Avatar
				os.Remove(oldPath)
			}

			// Use utils.SaveOptimizedImage
			avatarURL, err := utils.SaveOptimizedImage(avatarFile, "./uploads/avatars", userID, nil)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save avatar: " + err.Error()})
				return
			}

			// Update user avatar
			if err := database.DB.Model(&user).Update("avatar", avatarURL).Error; err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update avatar in database"})
				return
			}
		}
	}

	// Get updated user to return
	var user models.User
	if err := database.DB.
		Preload("Roles").
		Preload("SocialAccounts").
		Where("id = ?", userID).
		First(&user).Error; err != nil {
		c.JSON(http.StatusOK, gin.H{"message": "Profile updated successfully"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Profile updated successfully",
		"user":    user,
	})
}

// ChangePassword godoc
// @Summary Change password
// @Tags Profile
// @Security ApiKeyAuth
// @Accept json
// @Produce json
// @Param request body object true "Password change request"
// @Success 200 {object} map[string]interface{}
// @Router /profile/password [put]
func (h *ProfileHandler) ChangePassword(c *gin.Context) {
	userID := c.GetString("user_id")
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
		return
	}

	var input struct {
		CurrentPassword string `json:"current_password" binding:"required"`
		NewPassword     string `json:"new_password" binding:"required,min=6"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Get user
	var user models.User
	if err := database.DB.Preload("SocialAccounts").Where("id = ?", userID).First(&user).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}

	// Check if user is OAuth user
	if isOAuthUser(&user) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot change password for OAuth users (Google/GitHub login)"})
		return
	}

	// Verify current password
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(input.CurrentPassword)); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Current password is incorrect"})
		return
	}

	// Hash new password
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(input.NewPassword), bcrypt.DefaultCost)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})
		return
	}

	// Update password
	if err := database.DB.Model(&user).Update("password", string(hashedPassword)).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update password"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Password changed successfully"})
}

// ChangeEmail godoc
// @Summary Change email address
// @Tags Profile
// @Security ApiKeyAuth
// @Accept json
// @Produce json
// @Param request body object true "Email change request"
// @Success 200 {object} map[string]interface{}
// @Router /profile/email [put]
func (h *ProfileHandler) ChangeEmail(c *gin.Context) {
	userID := c.GetString("user_id")
	if userID == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
		return
	}

	var input struct {
		NewEmail string `json:"new_email" binding:"required,email"`
		Password string `json:"password"`
	}

	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Get user
	var user models.User
	if err := database.DB.Preload("SocialAccounts").Where("id = ?", userID).First(&user).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
		return
	}

	// Check if user is OAuth user
	if isOAuthUser(&user) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot change email for OAuth users (Google/GitHub login)"})
		return
	}

	// Verify password
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(input.Password)); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Password is incorrect"})
		return
	}

	// Check if new email already exists
	var existingUser models.User
	if err := database.DB.Where("email = ? AND id != ?", input.NewEmail, userID).First(&existingUser).Error; err == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Email already in use"})
		return
	}

	// Generate verification token
	verifyToken, err := utils.GenerateSecureToken(32)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate verification token"})
		return
	}

	// Update email and set as unverified
	falseBool := false
	updates := map[string]interface{}{
		"email":              input.NewEmail,
		"email_verified":     &falseBool,
		"email_verify_token": verifyToken,
	}

	if err := database.DB.Model(&user).Updates(updates).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update email"})
		return
	}

	// Send verification email
	go func() {
		if err := utils.SendVerificationEmail(input.NewEmail, verifyToken); err != nil {
			fmt.Printf("Failed to send verification email to %s: %v\n", input.NewEmail, err)
		}
	}()

	c.JSON(http.StatusOK, gin.H{
		"message":            "Email updated. Please verify your new email address.",
		"new_email":          input.NewEmail,
		"verification_token": verifyToken,
	})
}
