package middleware

import (
	"fmt"
	"sno/internal/service"
	"sno/internal/types"

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

// AdminConfig holds configuration for admin middleware
type AdminConfig struct {
	AdminService service.AdminService
}

// RequireRole middleware to check user roles
func RequireRole(roles ...types.UserRole) fiber.Handler {
	return func(c *fiber.Ctx) error {
		// Skip role checking for OPTIONS preflight requests
		if c.Method() == "OPTIONS" {
			return c.Next()
		}

		userData := GetTelegramUser(c)
		if userData == nil {
			return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
				"success": false,
				"message": "User not authenticated",
			})
		}

		// Check if user has any of the required roles
		userRole, err := getUserRole(c, userData.ID)
		if err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"success": false,
				"message": "Failed to verify user role",
			})
		}

		for _, role := range roles {
			if userRole == role {
				return c.Next()
			}
		}

		return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
			"success": false,
			"message": fmt.Sprintf("Insufficient permissions. Required role: one of %v", roles),
		})
	}
}

// RequireAdmin middleware to check if user is admin
func RequireAdmin(config AdminConfig) fiber.Handler {
	return RequireRole(types.RoleAdmin)
}

// RequireOperator middleware to check if user is operator or admin
func RequireOperator(config AdminConfig) fiber.Handler {
	return RequireRole(types.RoleAdmin, types.RoleOperator)
}

// getUserRole retrieves user role from database or cache
func getUserRole(c *fiber.Ctx, userID int64) (types.UserRole, error) {
	// For now, we'll check if user exists in admins table
	// In production, you might want to cache this in Redis

	// Try to get from context first (cached)
	if role, ok := c.Locals("user_role").(types.UserRole); ok {
		return role, nil
	}

	// Get admin service from context
	adminService, ok := c.Locals("admin_service").(service.AdminService)
	if !ok {
		// Fallback: if no admin service, assume user role
		return types.RoleUser, nil
	}

	// Check if user is admin
	role, err := adminService.GetUserRole(c.Context(), userID)
	if err != nil {
		// If user not found in admins table, they are regular user
		return types.RoleUser, nil
	}

	// Cache role in context
	c.Locals("user_role", role)

	return role, nil
}

// AdminMiddleware middleware for admin routes
func AdminMiddleware(config AdminConfig) fiber.Handler {
	return func(c *fiber.Ctx) error {
		// Store admin service in context for role checking
		c.Locals("admin_service", config.AdminService)

		// Apply auth middleware first
		return AuthMiddleware(AuthConfig{})(c)
	}
}

// WithAdminRoles applies both admin middleware and role checking
func WithAdminRoles(config AdminConfig, roles ...types.UserRole) fiber.Handler {
	return func(c *fiber.Ctx) error {
		// First, apply admin middleware (includes auth)
		if err := AdminMiddleware(config)(c); err != nil {
			return err
		}

		// Then check roles
		return RequireRole(roles...)(c)
	}
}

// Convenience functions
func IsAdmin(c *fiber.Ctx) bool {
	userData := GetTelegramUser(c)
	if userData == nil {
		return false
	}

	role, err := getUserRole(c, userData.ID)
	if err != nil {
		return false
	}

	return role == types.RoleAdmin
}

func IsOperator(c *fiber.Ctx) bool {
	userData := GetTelegramUser(c)
	if userData == nil {
		return false
	}

	role, err := getUserRole(c, userData.ID)
	if err != nil {
		return false
	}

	return role == types.RoleOperator || role == types.RoleAdmin
}

func GetCurrentUserRole(c *fiber.Ctx) (types.UserRole, error) {
	userData := GetTelegramUser(c)
	if userData == nil {
		return "", fmt.Errorf("user not authenticated")
	}

	return getUserRole(c, userData.ID)
}