package middlewares

import (
	"strings"

	"ares/services"

	"github.com/gofiber/fiber/v3"
)

const authClaimsKey = "auth_claims"

func RequireAuth(c fiber.Ctx) error {
	// First try Authorization header (Bearer JWT)
	authHeader := strings.TrimSpace(c.Get("Authorization"))
	if authHeader != "" {
		parts := strings.SplitN(authHeader, " ", 2)
		if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") || strings.TrimSpace(parts[1]) == "" {
			// If request originates from browser/HTMX, redirect to login instead of returning JSON
			if c.Get("HX-Request") == "true" {
				c.Set("HX-Redirect", "/login")
				return c.SendStatus(fiber.StatusOK)
			}
			accept := strings.ToLower(c.Get("Accept"))
			if strings.Contains(accept, "text/html") || strings.HasPrefix(c.Path(), "/admin") {
				return c.Redirect().To("/login")
			}
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid authorization format, expected: Bearer <token>"})
		}
		jwtService := services.NewJWTService()
		claims, err := jwtService.ValidateToken(strings.TrimSpace(parts[1]))
		if err != nil {
			if c.Get("HX-Request") == "true" {
				c.Set("HX-Redirect", "/login")
				return c.SendStatus(fiber.StatusOK)
			}
			accept := strings.ToLower(c.Get("Accept"))
			if strings.Contains(accept, "text/html") || strings.HasPrefix(c.Path(), "/admin") {
				return c.Redirect().To("/login")
			}
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid token"})
		}
		if claims.TokenType != services.TokenTypeAccess {
			if c.Get("HX-Request") == "true" {
				c.Set("HX-Redirect", "/login")
				return c.SendStatus(fiber.StatusOK)
			}
			accept := strings.ToLower(c.Get("Accept"))
			if strings.Contains(accept, "text/html") || strings.HasPrefix(c.Path(), "/admin") {
				return c.Redirect().To("/login")
			}
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "access token required"})
		}
		c.Locals(authClaimsKey, claims)
		return c.Next()
	}

	// Fallback: check cookie-based admin session (browser login) — expect signed JWT
	cookie := c.Cookies("admin_session")
	if cookie != "" {
		jwtService := services.NewJWTService()
		if claims, err := jwtService.ValidateToken(cookie); err == nil {
			if claims.TokenType == services.TokenTypeAccess {
				c.Locals(authClaimsKey, claims)
				return c.Next()
			}
		}
	}

	// Default unauthorized response: redirect to login for browser requests, JSON for API clients
	if c.Get("HX-Request") == "true" {
		c.Set("HX-Redirect", "/login")
		return c.SendStatus(fiber.StatusOK)
	}
	accept := strings.ToLower(c.Get("Accept"))
	if strings.Contains(accept, "text/html") || strings.HasPrefix(c.Path(), "/admin") {
		return c.Redirect().To("/login")
	}
	return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "authorization header is required"})
}

func RequireAdmin(c fiber.Ctx) error {
	claims, ok := GetAuthClaims(c)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}
	if !claims.IsAdmin {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "admin role required"})
	}
	return c.Next()
}

func RequireNormalUser(c fiber.Ctx) error {
	claims, ok := GetAuthClaims(c)
	if !ok {
		return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"})
	}
	if claims.IsAdmin {
		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "only normal users can access this endpoint"})
	}
	return c.Next()
}

func GetAuthClaims(c fiber.Ctx) (*services.JWTClaim, bool) {
	raw := c.Locals(authClaimsKey)
	claims, ok := raw.(*services.JWTClaim)
	return claims, ok
}
