package routes

import (
	"fmt"
	"strings"

	"dk/internal/components"
	"dk/internal/models/users"

	sushi "git.sharkk.net/Sharkk/Sushi"
	"git.sharkk.net/Sharkk/Sushi/auth"
	"git.sharkk.net/Sharkk/Sushi/password"
)

// RegisterAuthRoutes sets up authentication routes
func RegisterAuthRoutes(app *sushi.App) {
	// Public routes (no auth required)
	app.Get("/login", showLogin)
	app.Post("/login", processLogin)
	app.Get("/register", showRegister)
	app.Post("/register", processRegister)

	// Protected routes
	authed := app.Group("")
	authed.Use(auth.RequireAuth("/login"))
	authed.Post("/logout", processLogout)
}

// showLogin displays the login form
func showLogin(ctx sushi.Ctx) {
	sess := ctx.GetCurrentSession()
	var id string

	if formData, exists := sess.Get("form_data"); exists {
		if data, ok := formData.(map[string]string); ok {
			id = data["id"]
		}
	}
	sess.Delete("form_data")

	components.RenderPage(ctx, "Log In", "auth/login.html", map[string]any{
		"id": id,
	})
}

// processLogin handles login form submission
func processLogin(ctx sushi.Ctx) {
	email := strings.TrimSpace(string(ctx.PostArgs().Peek("id")))
	userPassword := string(ctx.PostArgs().Peek("password"))

	if email == "" || userPassword == "" {
		setFlashAndFormData(ctx, "Email and password are required", map[string]string{"id": email})
		ctx.Redirect("/login")
		return
	}

	user, err := authenticate(email, userPassword)
	if err != nil {
		setFlashAndFormData(ctx, "Invalid email or password", map[string]string{"id": email})
		ctx.Redirect("/login")
		return
	}

	// Update last online time when logging in
	user.UpdateLastOnline()

	ctx.Login(user.ID, user)

	// Set success message
	sess := ctx.GetCurrentSession()
	sess.SetFlash("success", fmt.Sprintf("Welcome back, %s!", user.Username))

	ctx.Redirect("/")
}

// showRegister displays the registration form
func showRegister(ctx sushi.Ctx) {
	sess := ctx.GetCurrentSession()
	var username, email string

	if formData, exists := sess.Get("form_data"); exists {
		if data, ok := formData.(map[string]string); ok {
			username = data["username"]
			email = data["email"]
		}
	}
	sess.Delete("form_data")

	components.RenderPage(ctx, "Register", "auth/register.html", map[string]any{
		"username":      username,
		"email":         email,
		"error_message": sess.GetFlashMessage("error"),
	})
}

// processRegister handles registration form submission
func processRegister(ctx sushi.Ctx) {
	username := strings.TrimSpace(ctx.Form("username").String())
	email := strings.TrimSpace(ctx.Form("email").String())
	userPassword := ctx.Form("password").String()
	confirmPassword := ctx.Form("confirm_password").String()

	formData := map[string]string{
		"username": username,
		"email":    email,
	}

	if err := validateRegistration(username, email, userPassword, confirmPassword); err != nil {
		setFlashAndFormData(ctx, err.Error(), formData)
		ctx.Redirect("/register")
		return
	}

	// Check if username already exists
	if _, err := users.ByUsername(username); err == nil {
		setFlashAndFormData(ctx, "Username already exists", formData)
		ctx.Redirect("/register")
		return
	}

	// Check if email already exists
	if _, err := users.ByEmail(email); err == nil {
		setFlashAndFormData(ctx, "Email already registered", formData)
		ctx.Redirect("/register")
		return
	}

	// Create new user
	user := users.New()
	user.Username = username
	user.Email = email
	user.Password = password.HashPassword(userPassword)
	user.ClassID = 1
	user.Auth = 1

	// Validate before inserting
	if err := user.Validate(); err != nil {
		setFlashAndFormData(ctx, fmt.Sprintf("Invalid user data: %s", err.Error()), formData)
		ctx.Redirect("/register")
		return
	}

	if err := user.Insert(); err != nil {
		setFlashAndFormData(ctx, "Failed to create account", formData)
		ctx.Redirect("/register")
		return
	}

	// Auto-login after registration
	ctx.Login(user.ID, user)

	// Set success message
	sess := ctx.GetCurrentSession()
	sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username))

	ctx.Redirect("/")
}

// processLogout handles logout
func processLogout(ctx sushi.Ctx) {
	ctx.Logout()
	ctx.Redirect("/")
}

// Helper functions

func validateRegistration(username, email, password, confirmPassword string) error {
	if username == "" {
		return fmt.Errorf("username is required")
	}
	if len(username) < 3 {
		return fmt.Errorf("username must be at least 3 characters")
	}
	if email == "" {
		return fmt.Errorf("email is required")
	}
	if !strings.Contains(email, "@") {
		return fmt.Errorf("invalid email address")
	}
	if password == "" {
		return fmt.Errorf("password is required")
	}
	if len(password) < 6 {
		return fmt.Errorf("password must be at least 6 characters")
	}
	if password != confirmPassword {
		return fmt.Errorf("passwords do not match")
	}
	return nil
}

func setFlashAndFormData(ctx sushi.Ctx, message string, formData map[string]string) {
	sess := ctx.GetCurrentSession()
	sess.SetFlash("error", message)
	sess.Set("form_data", formData)
}

func authenticate(usernameOrEmail, plainPassword string) (*users.User, error) {
	var user *users.User
	var err error

	// Try username first
	user, err = users.ByUsername(usernameOrEmail)
	if err != nil {
		// If username not found, try email
		user, err = users.ByEmail(usernameOrEmail)
		if err != nil {
			return nil, fmt.Errorf("user not found")
		}
	}

	isValid, err := password.VerifyPassword(plainPassword, user.Password)
	if err != nil {
		return nil, fmt.Errorf("password verification error: %w", err)
	}
	if !isValid {
		return nil, fmt.Errorf("invalid password")
	}

	return user, nil
}
