package accounts

import (
	"strconv"
	"strings"
	"time"

	"github.com/gofiber/fiber/v3"
	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"

	"goimgApi/configs"
)

type AuthReq struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

// Register godoc
// @Summary Register a new user
// @Description Register a new user with email and password
// @Tags Auth
// @Accept multipart/form-data
// @Produce json
// @Param email formData string true "Email address"
// @Param password formData string true "Password (min 6 chars)"
// @Success 201 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Router /auth/register [post]
func Register(c fiber.Ctx) error {
	email := c.FormValue("email")
	password := c.FormValue("password")

	if email == "" || len(password) < 6 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Email required and password min 6 chars"})
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to hash password"})
	}

	user := User{
		Email:        email,
		PasswordHash: string(hash),
	}

	if err := configs.DB.Create(&user).Error; err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Email might be already in use"})
	}

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{"message": "User registered", "user_id": user.ID})
}

// Login godoc
// @Summary Login
// @Description Authenticate user and get JWT
// @Tags Auth
// @Accept multipart/form-data
// @Produce json
// @Param email formData string true "Email address"
// @Param password formData string true "Password"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Router /auth/login [post]
func Login(c fiber.Ctx) error {
	email := c.FormValue("email")
	password := c.FormValue("password")

	var user User
	if err := configs.DB.Where("email = ?", email).First(&user).Error; err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid credentials"})
	}

	if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid credentials"})
	}

	role := roleFromUser(user)
	access, refresh, err := GenerateTokens(user.ID, role)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Token generation failed"})
	}

	return c.JSON(fiber.Map{
		"access_token":  access,
		"refresh_token": refresh,
		"user": fiber.Map{
			"id":    user.ID,
			"email": user.Email,
			"role":  role,
		},
	})
}

type RefreshReq struct {
	RefreshToken string `json:"refresh_token"`
}

// Refresh godoc
// @Summary Refresh JWT
// @Description Get a new access token using a valid refresh token
// @Tags Auth
// @Accept multipart/form-data
// @Produce json
// @Param refresh_token formData string true "Refresh token"
// @Success 200 {object} map[string]interface{}
// @Failure 401 {object} map[string]interface{}
// @Failure 400 {object} map[string]interface{}
// @Failure 500 {object} map[string]interface{}
// @Router /auth/refresh [post]
func Refresh(c fiber.Ctx) error {
	refreshToken := c.FormValue("refresh_token")

	if refreshToken == "" {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Refresh token required"})
	}

	userID, err := ParseRefreshToken(refreshToken)
	if err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired refresh token"})
	}

	var user User
	if err := configs.DB.First(&user, userID).Error; err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "User not found"})
	}

	acc, ref, err := GenerateTokens(userID, roleFromUser(user))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Token generation failed"})
	}

	return c.JSON(fiber.Map{
		"access_token":  acc,
		"refresh_token": ref,
	})
}

// JWTMiddleware extracts user_id from access token
func JWTMiddleware(c fiber.Ctx) error {
	authHeader := c.Get("Authorization")
	if authHeader == "" {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
	}

	// Swagger UI esnekliği için "Bearer " prefix'ini isteğe bağlı kılıyoruz.
	tokenStr := strings.TrimSpace(strings.TrimPrefix(authHeader, "Bearer"))
	claims, err := parseAccessClaims(tokenStr)
	if err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired token"})
	}

	// Set userID context
	c.Locals("user_id", claims.UserID)
	c.Locals("role", claims.Role)
	return c.Next()
}

// AdminMiddleware ensures the logged-in user is an admin
func AdminMiddleware(c fiber.Ctx) error {
	userIDVal := c.Locals("user_id")
	if userIDVal == nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Unauthorized"})
	}

	var user User
	if err := configs.DB.First(&user, userIDVal).Error; err != nil {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "User not found"})
	}

	if !user.IsAdmin {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Admin privileges required"})
	}

	return c.Next()
}

type CreateApiTokenReq struct {
	ExpiresInDays int `json:"expires_in_days"`
}

// CreateApiToken godoc
// @Summary Create API Token
// @Description Creates an API Token for a user (Admin ONLY).
// @Tags Admin
// @Accept multipart/form-data
// @Produce json
// @Security BearerAuth
// @Param id path int true "User ID"
// @Param expires_in_days formData int false "Expiration in days (0 or omit for never)"
// @Success 200 {object} map[string]interface{}
// @Failure 401 {object} map[string]interface{}
// @Failure 403 {object} map[string]interface{}
// @Failure 404 {object} map[string]interface{}
// @Router /admin/users/{id}/api-token [post]
func CreateApiToken(c fiber.Ctx) error {
	targetID := c.Params("id")

	expiresInDays := 0
	if f := c.FormValue("expires_in_days"); f != "" {
		if val, err := strconv.Atoi(f); err == nil {
			expiresInDays = val
		}
	}
	// Fallback eklendi: Eger FormData gelmediyse Query'den parametreyi deneriz
	if q := c.Query("expires_in_days"); q != "" {
		if val, err := strconv.Atoi(q); err == nil {
			expiresInDays = val
		}
	}

	var user User
	if err := configs.DB.First(&user, targetID).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
	}

	// Generate and update
	token := uuid.New().String()
	user.ApiToken = token

	if expiresInDays > 0 {
		exp := time.Now().Add(time.Duration(expiresInDays) * 24 * time.Hour)
		user.ApiTokenExpiresAt = &exp
	} else {
		user.ApiTokenExpiresAt = nil
	}

	if err := configs.DB.Save(&user).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to save API token"})
	}

	responseMap := fiber.Map{
		"message":   "API token created successfully",
		"api_token": token,
	}

	if user.ApiTokenExpiresAt != nil {
		responseMap["expires_at"] = user.ApiTokenExpiresAt
	} else {
		responseMap["expires_at"] = "never"
	}

	return c.JSON(responseMap)
}

func roleFromUser(user User) string {
	if user.IsAdmin {
		return RoleAdmin
	}

	return RoleUser
}
