package auth

import (
	"context"
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"log"
	"os"
	"time"

	"github.com/bigtux/ophion/internal/security"
	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
)

// ═══════════════════════════════════════════════════════════
// 🔐 AUTH HANDLERS
// ═══════════════════════════════════════════════════════════

type AuthHandler struct {
	db          *sql.DB
	authService *AuthService
}

type User struct {
	ID           string    `json:"id"`
	Email        string    `json:"email"`
	PasswordHash string    `json:"-"`
	Role         string    `json:"role"`
	CreatedAt    time.Time `json:"created_at"`
}

type APIKey struct {
	ID        string     `json:"id"`
	KeyHash   string     `json:"-"`
	Prefix    string     `json:"prefix"`
	Name      string     `json:"name"`
	UserID    string     `json:"user_id"`
	CreatedAt time.Time  `json:"created_at"`
	LastUsed  *time.Time `json:"last_used,omitempty"`
}

// NewAuthHandler cria novo handler de autenticação
func NewAuthHandler(db *sql.DB, jwtSecret string) *AuthHandler {
	config := AuthConfig{
		JWTSecret:         []byte(jwtSecret),
		JWTExpiration:     24 * time.Hour,
		RefreshExpiration: 7 * 24 * time.Hour,
		Issuer:            "ophion",
	}

	// Implementação simples do APIKeyStore
	store := &DBAPIKeyStore{db: db}

	authService := NewAuthService(config, nil, store)

	return &AuthHandler{
		db:          db,
		authService: authService,
	}
}

// GetAuthService retorna o serviço de autenticação
func (h *AuthHandler) GetAuthService() *AuthService {
	return h.authService
}

// ─────────────────────────────────────────────────────────────
// Login
// ─────────────────────────────────────────────────────────────

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

type LoginResponse struct {
	Token     string `json:"token"`
	ExpiresIn int    `json:"expires_in"`
	User      struct {
		ID    string `json:"id"`
		Email string `json:"email"`
		Role  string `json:"role"`
	} `json:"user"`
}

func (h *AuthHandler) Login(c *fiber.Ctx) error {
	var req LoginRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
	}

	if req.Email == "" || req.Password == "" {
		return c.Status(400).JSON(fiber.Map{"error": "Email and password are required"})
	}

	// Sanitize email
	email, err := security.SanitizeEmail(req.Email)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Invalid email format"})
	}

	// Check brute force protection
	allowed, remaining, _ := h.authService.CheckLoginAllowed(email)
	if !allowed {
		return c.Status(429).JSON(fiber.Map{
			"error":   "Too many failed attempts",
			"message": "Account temporarily locked. Try again in " + remaining.String(),
		})
	}

	// Find user
	var user User
	err = h.db.QueryRow(`
		SELECT id, email, password_hash, role, created_at
		FROM users WHERE email = $1
	`, email).Scan(&user.ID, &user.Email, &user.PasswordHash, &user.Role, &user.CreatedAt)

	if err == sql.ErrNoRows {
		h.authService.RecordLoginAttempt(email, false)
		return c.Status(401).JSON(fiber.Map{"error": "Invalid credentials"})
	}
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Database error"})
	}

	// Verify password
	if !security.VerifyPassword(req.Password, user.PasswordHash) {
		blocked, remaining := h.authService.RecordLoginAttempt(email, false)
		if blocked {
			return c.Status(429).JSON(fiber.Map{
				"error":   "Too many failed attempts",
				"message": "Account temporarily locked",
			})
		}
		return c.Status(401).JSON(fiber.Map{
			"error":              "Invalid credentials",
			"remaining_attempts": remaining,
		})
	}

	// Successful login
	h.authService.RecordLoginAttempt(email, true)

	// Generate token
	token, _, err := h.authService.GenerateTokenPair(user.ID, "", user.Email, user.Role)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Failed to generate token"})
	}

	resp := LoginResponse{
		Token:     token,
		ExpiresIn: 86400, // 24 hours
	}
	resp.User.ID = user.ID
	resp.User.Email = user.Email
	resp.User.Role = user.Role

	return c.JSON(resp)
}

// ─────────────────────────────────────────────────────────────
// Register
// ─────────────────────────────────────────────────────────────

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

func (h *AuthHandler) Register(c *fiber.Ctx) error {
	var req RegisterRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
	}

	if req.Email == "" || req.Password == "" {
		return c.Status(400).JSON(fiber.Map{"error": "Email and password are required"})
	}

	// Sanitize email
	email, err := security.SanitizeEmail(req.Email)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Invalid email format"})
	}

	// Check password (relaxed policy for simplicity)
	if len(req.Password) < 6 {
		return c.Status(400).JSON(fiber.Map{"error": "Password must be at least 6 characters"})
	}

	// Check if any users exist (first user becomes admin)
	var userCount int
	h.db.QueryRow(`SELECT COUNT(*) FROM users`).Scan(&userCount)

	role := "user"
	if userCount == 0 {
		role = "admin"
	}

	// Check if email already exists
	var existingID string
	err = h.db.QueryRow(`SELECT id FROM users WHERE email = $1`, email).Scan(&existingID)
	if err != sql.ErrNoRows {
		return c.Status(409).JSON(fiber.Map{"error": "Email already registered"})
	}

	// Hash password
	passwordHash, err := security.HashPassword(req.Password)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Failed to hash password"})
	}

	// Create user
	userID := uuid.New().String()
	_, err = h.db.Exec(`
		INSERT INTO users (id, email, password_hash, role, created_at)
		VALUES ($1, $2, $3, $4, NOW())
	`, userID, email, passwordHash, role)
	if err != nil {
		log.Printf("Error creating user: %v", err)
		return c.Status(500).JSON(fiber.Map{"error": "Failed to create user"})
	}

	// Generate token
	token, _, err := h.authService.GenerateTokenPair(userID, "", email, role)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Failed to generate token"})
	}

	return c.Status(201).JSON(fiber.Map{
		"message": "User registered successfully",
		"token":   token,
		"user": fiber.Map{
			"id":    userID,
			"email": email,
			"role":  role,
		},
	})
}

// ─────────────────────────────────────────────────────────────
// API Keys
// ─────────────────────────────────────────────────────────────

type CreateAPIKeyRequest struct {
	Name string `json:"name"`
}

func (h *AuthHandler) CreateAPIKey(c *fiber.Ctx) error {
	var req CreateAPIKeyRequest
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
	}

	if req.Name == "" {
		return c.Status(400).JSON(fiber.Map{"error": "Name is required"})
	}

	userID := GetUserID(c)
	if userID == "" {
		return c.Status(401).JSON(fiber.Map{"error": "Unauthorized"})
	}

	// Generate API key
	key, keyHash, prefix := security.GenerateAPIKey()

	keyID := uuid.New().String()
	_, err := h.db.Exec(`
		INSERT INTO api_keys (id, key_hash, prefix, name, user_id, created_at)
		VALUES ($1, $2, $3, $4, $5, NOW())
	`, keyID, keyHash, prefix, req.Name, userID)
	if err != nil {
		log.Printf("Error creating API key: %v", err)
		return c.Status(500).JSON(fiber.Map{"error": "Failed to create API key"})
	}

	return c.Status(201).JSON(fiber.Map{
		"message": "API key created successfully",
		"key":     key, // Only shown once!
		"id":      keyID,
		"name":    req.Name,
		"prefix":  prefix,
	})
}

func (h *AuthHandler) ListAPIKeys(c *fiber.Ctx) error {
	userID := GetUserID(c)
	role := GetRole(c)

	var rows *sql.Rows
	var err error

	// Admin can see all keys
	if role == "admin" {
		rows, err = h.db.Query(`
			SELECT id, prefix, name, user_id, created_at, last_used
			FROM api_keys ORDER BY created_at DESC
		`)
	} else {
		rows, err = h.db.Query(`
			SELECT id, prefix, name, user_id, created_at, last_used
			FROM api_keys WHERE user_id = $1 ORDER BY created_at DESC
		`, userID)
	}

	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Database error"})
	}
	defer rows.Close()

	var keys []fiber.Map
	for rows.Next() {
		var k APIKey
		if err := rows.Scan(&k.ID, &k.Prefix, &k.Name, &k.UserID, &k.CreatedAt, &k.LastUsed); err == nil {
			keys = append(keys, fiber.Map{
				"id":         k.ID,
				"prefix":     k.Prefix,
				"name":       k.Name,
				"user_id":    k.UserID,
				"created_at": k.CreatedAt,
				"last_used":  k.LastUsed,
			})
		}
	}

	if keys == nil {
		keys = []fiber.Map{}
	}

	return c.JSON(fiber.Map{"api_keys": keys})
}

func (h *AuthHandler) DeleteAPIKey(c *fiber.Ctx) error {
	keyID := c.Params("id")
	userID := GetUserID(c)
	role := GetRole(c)

	var ownerID string
	err := h.db.QueryRow(`SELECT user_id FROM api_keys WHERE id = $1`, keyID).Scan(&ownerID)
	if err == sql.ErrNoRows {
		return c.Status(404).JSON(fiber.Map{"error": "API key not found"})
	}
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Database error"})
	}

	// Check permission
	if role != "admin" && ownerID != userID {
		return c.Status(403).JSON(fiber.Map{"error": "Permission denied"})
	}

	_, err = h.db.Exec(`DELETE FROM api_keys WHERE id = $1`, keyID)
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": "Failed to delete API key"})
	}

	return c.JSON(fiber.Map{"message": "API key deleted"})
}

// ─────────────────────────────────────────────────────────────
// Me (current user)
// ─────────────────────────────────────────────────────────────

func (h *AuthHandler) Me(c *fiber.Ctx) error {
	userID := GetUserID(c)
	email := c.Locals("email")
	role := GetRole(c)

	return c.JSON(fiber.Map{
		"id":    userID,
		"email": email,
		"role":  role,
	})
}

// ═══════════════════════════════════════════════════════════
// 🗄️ DB API KEY STORE
// ═══════════════════════════════════════════════════════════

type DBAPIKeyStore struct {
	db *sql.DB
}

func (s *DBAPIKeyStore) ValidateKey(ctx context.Context, keyHash string) (*APIKeyInfo, error) {
	var info APIKeyInfo
	err := s.db.QueryRowContext(ctx, `
		SELECT id, name FROM api_keys WHERE key_hash = $1
	`, keyHash).Scan(&info.ID, &info.Name)

	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	info.Scopes = []string{"*"} // Full access for now
	return &info, nil
}

func (s *DBAPIKeyStore) UpdateLastUsed(ctx context.Context, keyID string) error {
	_, err := s.db.ExecContext(ctx, `
		UPDATE api_keys SET last_used = NOW() WHERE id = $1
	`, keyID)
	return err
}

// ═══════════════════════════════════════════════════════════
// 🔧 ADMIN USER SETUP
// ═══════════════════════════════════════════════════════════

// CreateDefaultAdmin cria usuário admin padrão se não existir
func CreateDefaultAdmin(db *sql.DB) error {
	// Check if any users exist
	var count int
	if err := db.QueryRow(`SELECT COUNT(*) FROM users`).Scan(&count); err != nil {
		// Table might not exist yet
		return nil
	}

	if count > 0 {
		return nil // Admin already exists
	}

	adminEmail := "admin@ophion.local"
	adminPassword := os.Getenv("ADMIN_PASSWORD")
	if adminPassword == "" {
		adminPassword = "ophion123"
	}

	passwordHash, err := security.HashPassword(adminPassword)
	if err != nil {
		return err
	}

	userID := uuid.New().String()
	_, err = db.Exec(`
		INSERT INTO users (id, email, password_hash, role, created_at)
		VALUES ($1, $2, $3, 'admin', NOW())
		ON CONFLICT (email) DO NOTHING
	`, userID, adminEmail, passwordHash)

	if err != nil {
		return err
	}

	log.Printf("✓ Default admin user created: %s", adminEmail)
	return nil
}

// HashAPIKey creates SHA256 hash of API key
func HashAPIKey(key string) string {
	hash := sha256.Sum256([]byte(key))
	return hex.EncodeToString(hash[:])
}
