package api

import (
	"encoding/json"
	"errors"
	"net/http"

	"dance-lessons-coach/pkg/user"
	"dance-lessons-coach/pkg/validation"

	"github.com/go-chi/chi/v5"
	"github.com/rs/zerolog/log"
)

// AuthHandler handles authentication-related HTTP requests
type AuthHandler struct {
	authService user.AuthService
	userService user.UserService
	validator   *validation.Validator
}

// NewAuthHandler creates a new authentication handler
func NewAuthHandler(authService user.AuthService, userService user.UserService, validator *validation.Validator) *AuthHandler {
	return &AuthHandler{
		authService: authService,
		userService: userService,
		validator:   validator,
	}
}

// RegisterRoutes registers authentication routes
func (h *AuthHandler) RegisterRoutes(router chi.Router) {
	router.Post("/login", h.handleLogin)
	router.Post("/register", h.handleRegister)
	router.Post("/password-reset/request", h.handlePasswordResetRequest)
	router.Post("/password-reset/complete", h.handlePasswordResetComplete)
}

// writeValidationError writes a structured validation error response
func (h *AuthHandler) writeValidationError(w http.ResponseWriter, err error) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusBadRequest)

	// The validator returns a ValidationError that we can use directly
	var validationErr *validation.ValidationError
	if errors.As(err, &validationErr) {
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error":   "validation_failed",
			"message": "Invalid request data",
			"details": validationErr.Messages,
		})
		return
	}

	// Fallback for other error types
	json.NewEncoder(w).Encode(map[string]interface{}{
		"error":   "validation_failed",
		"message": err.Error(),
	})
}

// LoginRequest represents a login request
type LoginRequest struct {
	Username string `json:"username" validate:"required,min=3,max=50"`
	Password string `json:"password" validate:"required,min=6"`
}

// LoginResponse represents a login response
type LoginResponse struct {
	Token string `json:"token"`
}

// handleLogin godoc
//
//	@Summary		User login
//	@Description	Authenticate user or admin and return JWT token. Supports both regular users and admin authentication.
//	@Tags			API/v1/User
//	@Accept			json
//	@Produce		json
//	@Param			request	body		LoginRequest		true	"Login credentials"
//	@Success		200		{object}	LoginResponse		"Successful authentication"
//	@Failure		400		{object}	map[string]string	"Invalid request"
//	@Failure		401		{object}	map[string]string	"Invalid credentials"
//	@Failure		500		{object}	map[string]string	"Server error"
//	@Router			/v1/auth/login [post]
func (h *AuthHandler) handleLogin(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var req LoginRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, `{"error":"invalid_request","message":"Invalid JSON request body"}`, http.StatusBadRequest)
		return
	}

	// Validate request using validator
	if h.validator != nil {
		if err := h.validator.Validate(req); err != nil {
			h.writeValidationError(w, err)
			return
		}
	}

	// Try unified authentication (regular user first, then admin fallback)
	var authenticatedUser *user.User
	var authError error

	// Try regular user authentication first
	authenticatedUser, authError = h.authService.Authenticate(ctx, req.Username, req.Password)

	// If regular auth fails, try admin authentication
	if authError != nil {
		authenticatedUser, authError = h.authService.AdminAuthenticate(ctx, req.Password)
	}

	// If both authentication methods failed
	if authError != nil {
		log.Trace().Ctx(ctx).Err(authError).Str("username", req.Username).Msg("Authentication failed")
		http.Error(w, `{"error":"invalid_credentials","message":"Invalid username or password"}`, http.StatusUnauthorized)
		return
	}

	// Generate JWT token using the authenticated user (regular or admin)
	token, err := h.authService.GenerateJWT(ctx, authenticatedUser)
	if err != nil {
		log.Error().Ctx(ctx).Err(err).Msg("Failed to generate JWT token")
		http.Error(w, `{"error":"server_error","message":"Failed to generate authentication token"}`, http.StatusInternalServerError)
		return
	}

	// Return token
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(LoginResponse{Token: token})
}

// RegisterRequest represents a user registration request
type RegisterRequest struct {
	Username string `json:"username" validate:"required,min=3,max=50"`
	Password string `json:"password" validate:"required,min=6,max=100"`
}

// handleRegister godoc
//
//	@Summary		User registration
//	@Description	Register a new user account
//	@Tags			API/v1/User
//	@Accept			json
//	@Produce		json
//	@Param			request	body		RegisterRequest		true	"Registration details"
//	@Success		201		{object}	map[string]string	"User created"
//	@Failure		400		{object}	map[string]string	"Invalid request"
//	@Failure		409		{object}	map[string]string	"Username already taken"
//	@Failure		500		{object}	map[string]string	"Server error"
//	@Router			/v1/auth/register [post]
func (h *AuthHandler) handleRegister(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var req RegisterRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, `{"error":"invalid_request","message":"Invalid JSON request body"}`, http.StatusBadRequest)
		return
	}

	// Validate request using validator
	if h.validator != nil {
		if err := h.validator.Validate(req); err != nil {
			h.writeValidationError(w, err)
			return
		}
	}

	// Check if user already exists
	exists, err := h.userService.UserExists(ctx, req.Username)
	if err != nil {
		log.Error().Ctx(ctx).Err(err).Msg("Failed to check if user exists")
		http.Error(w, `{"error":"server_error","message":"Failed to process registration"}`, http.StatusInternalServerError)
		return
	}
	if exists {
		http.Error(w, `{"error":"user_exists","message":"Username already taken"}`, http.StatusConflict)
		return
	}

	// Hash password
	hashedPassword, err := h.userService.HashPassword(ctx, req.Password)
	if err != nil {
		log.Error().Ctx(ctx).Err(err).Msg("Failed to hash password")
		http.Error(w, `{"error":"server_error","message":"Failed to process registration"}`, http.StatusInternalServerError)
		return
	}

	// Create user
	newUser := &user.User{
		Username:     req.Username,
		PasswordHash: hashedPassword,
		IsAdmin:      false,
	}

	if err := h.userService.CreateUser(ctx, newUser); err != nil {
		log.Error().Ctx(ctx).Err(err).Msg("Failed to create user")
		http.Error(w, `{"error":"server_error","message":"Failed to create user"}`, http.StatusInternalServerError)
		return
	}

	// Return success
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]string{"message": "User registered successfully"})
}

// PasswordResetRequest represents a password reset request
type PasswordResetRequest struct {
	Username string `json:"username" validate:"required,min=3,max=50"`
}

// handlePasswordResetRequest godoc
//
//	@Summary		Request password reset
//	@Description	Initiate password reset process for a user
//	@Tags			API/v1/User
//	@Accept			json
//	@Produce		json
//	@Param			request	body		PasswordResetRequest	true	"Password reset request"
//	@Success		200		{object}	map[string]string		"Reset allowed"
//	@Failure		400		{object}	map[string]string		"Invalid request"
//	@Failure		500		{object}	map[string]string		"Server error"
//	@Router			/v1/auth/password-reset/request [post]
func (h *AuthHandler) handlePasswordResetRequest(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var req PasswordResetRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, `{"error":"invalid_request","message":"Invalid JSON request body"}`, http.StatusBadRequest)
		return
	}

	// Validate request using validator
	if h.validator != nil {
		if err := h.validator.Validate(req); err != nil {
			h.writeValidationError(w, err)
			return
		}
	}

	// Request password reset
	if err := h.userService.RequestPasswordReset(ctx, req.Username); err != nil {
		log.Error().Ctx(ctx).Err(err).Msg("Failed to request password reset")
		http.Error(w, `{"error":"server_error","message":"Failed to process password reset request"}`, http.StatusInternalServerError)
		return
	}

	// Return success
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"message": "Password reset allowed, user can now reset password"})
}

// PasswordResetCompleteRequest represents a password reset completion request
type PasswordResetCompleteRequest struct {
	Username    string `json:"username" validate:"required,min=3,max=50"`
	NewPassword string `json:"new_password" validate:"required,min=6,max=100"`
}

// handlePasswordResetComplete godoc
//
//	@Summary		Complete password reset
//	@Description	Complete password reset with new password
//	@Tags			API/v1/User
//	@Accept			json
//	@Produce		json
//	@Param			request	body		PasswordResetCompleteRequest	true	"Password reset completion"
//	@Success		200		{object}	map[string]string				"Password updated"
//	@Failure		400		{object}	map[string]string				"Invalid request"
//	@Failure		500		{object}	map[string]string				"Server error"
//	@Router			/v1/auth/password-reset/complete [post]
func (h *AuthHandler) handlePasswordResetComplete(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	var req PasswordResetCompleteRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, `{"error":"invalid_request","message":"Invalid JSON request body"}`, http.StatusBadRequest)
		return
	}

	// Validate request using validator
	if h.validator != nil {
		if err := h.validator.Validate(req); err != nil {
			h.writeValidationError(w, err)
			return
		}
	}

	// Complete password reset
	if err := h.userService.CompletePasswordReset(ctx, req.Username, req.NewPassword); err != nil {
		log.Error().Ctx(ctx).Err(err).Msg("Failed to complete password reset")
		http.Error(w, `{"error":"server_error","message":"Failed to complete password reset"}`, http.StatusInternalServerError)
		return
	}

	// Return success
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"message": "Password reset completed successfully"})
}
