package auth

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

	"api_yal/internal/logger"
	"api_yal/internal/middleware"

	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
)

const (
	RefreshTokenCookieName = "refresh_token"
	RefreshTokenExpiration = 7 * 24 * time.Hour
)

// AuthHandler обработчик для аутентификации
type AuthHandler struct {
	authService AuthService
	validator   *validator.Validate
}

// NewAuthHandler создает новый экземпляр AuthHandler
func NewAuthHandler(authService AuthService) *AuthHandler {
	return &AuthHandler{
		authService: authService,
		validator:   validator.New(),
	}
}

// setRefreshTokenCookie устанавливает HttpOnly cookie с refresh token
func (h *AuthHandler) setRefreshTokenCookie(w http.ResponseWriter, refreshToken string) {
	http.SetCookie(w, &http.Cookie{
		Name:     RefreshTokenCookieName,
		Value:    refreshToken,
		Path:     "/",
		HttpOnly: true,
		Secure:   true,
		SameSite: http.SameSiteStrictMode,
		MaxAge:   int(RefreshTokenExpiration.Seconds()),
	})
}

// clearRefreshTokenCookie удаляет refresh token cookie
func (h *AuthHandler) clearRefreshTokenCookie(w http.ResponseWriter) {
	http.SetCookie(w, &http.Cookie{
		Name:     RefreshTokenCookieName,
		Value:    "",
		Path:     "/",
		HttpOnly: true,
		Secure:   true,
		SameSite: http.SameSiteStrictMode,
		MaxAge:   -1,
	})
}

// Register регистрация аккаунта пользователя
func (h *AuthHandler) Register(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Info("Начало обработки запроса регистрации")

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

	if err := h.validator.Struct(req); err != nil {
		h.handleValidationError(w, err)
		return
	}

	response, err := h.authService.Register(req)
	if err != nil {
		l.Error("Ошибка регистрации", zap.Error(err))

		status := http.StatusInternalServerError
		message := "Registration failed"

		if errors.Is(err, ErrUserAlreadyExists) {
			status = http.StatusConflict
			message = "User with this email already exists"
		}

		http.Error(w, message, status)
		return
	}

	l.Info("Завершение обработки запроса регистрации")
	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(response)
}

// Login вход пользователя
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Debug("Начало обработки запроса входа")

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

	if err := h.validator.Struct(req); err != nil {
		h.handleValidationError(w, err)
		return
	}

	response, refreshToken, err := h.authService.Login(req)
	if err != nil {
		l.Error("Ошибка входа", zap.Error(err))

		status := http.StatusUnauthorized
		message := "Login failed"

		if errors.Is(err, ErrUserNotFound) {
			message = "User not found"
		} else if errors.Is(err, ErrInvalidPassword) {
			message = "Invalid password"
		}

		http.Error(w, message, status)
		return
	}

	h.setRefreshTokenCookie(w, refreshToken)

	l.Debug("Завершение обработки запроса входа")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(response)
}

// RefreshToken обновление токена
func (h *AuthHandler) RefreshToken(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Info("Начало обработки запроса обновления токена")

	var refreshToken string

	cookie, err := r.Cookie(RefreshTokenCookieName)
	if err == nil && cookie != nil {
		refreshToken = cookie.Value
	}

	if refreshToken == "" {
		var req RefreshTokenRequest
		if err := json.NewDecoder(r.Body).Decode(&req); err == nil {
			refreshToken = req.RefreshToken
		}
	}

	if refreshToken == "" {
		http.Error(w, "Refresh token required", http.StatusBadRequest)
		return
	}

	response, err := h.authService.RefreshToken(refreshToken)
	if err != nil {
		l.Error("Ошибка обновления токена", zap.Error(err))

		if errors.Is(err, ErrInvalidToken) || errors.Is(err, ErrTokenExpired) {
			h.clearRefreshTokenCookie(w)
			http.Error(w, "Invalid or expired refresh token", http.StatusUnauthorized)
			return
		}

		http.Error(w, "Token refresh failed", http.StatusUnauthorized)
		return
	}

	// Генерируем новый refresh token
	newRefreshToken, err := h.authService.GenerateRefreshTokenForUser(response.User.ID)
	if err != nil {
		l.Error("Ошибка генерации нового refresh token", zap.Error(err))
		http.Error(w, "Failed to generate refresh token", http.StatusInternalServerError)
		return
	}

	h.setRefreshTokenCookie(w, newRefreshToken)

	l.Info("Завершение обработки запроса обновления токена")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(response)
}

// Logout выход пользователя
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Info("Начало обработки запроса выхода")

	userID, ok := r.Context().Value(middleware.UserIDKey).(uint)
	if !ok {
		http.Error(w, "Unauthorized", http.StatusUnauthorized)
		return
	}

	if err := h.authService.Logout(userID); err != nil {
		l.Error("Ошибка выхода", zap.Error(err))
		http.Error(w, "Logout failed", http.StatusInternalServerError)
		return
	}

	h.clearRefreshTokenCookie(w)

	l.Info("Завершение обработки запроса выхода")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Successfully logged out",
	})
}

// RequestPasswordReset запрос на сброс пароля
func (h *AuthHandler) RequestPasswordReset(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Info("Начало обработки запроса сброса пароля")

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

	if err := h.validator.Struct(req); err != nil {
		h.handleValidationError(w, err)
		return
	}

	resetToken, err := h.authService.RequestPasswordReset(req.Email)
	if err != nil {
		l.Error("Ошибка запроса сброса пароля", zap.Error(err))

		status := http.StatusInternalServerError
		message := "Password reset request failed"

		if errors.Is(err, ErrUserNotFound) {
			// Для безопасности не сообщаем, что пользователь не найден
			// Просто возвращаем успешный ответ
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(map[string]string{
				"message": "If the email exists, a reset link has been sent",
			})
			return
		}

		http.Error(w, message, status)
		return
	}

	// В реальном приложении здесь нужно отправить email с токеном
	// Для тестирования возвращаем токен в ответе
	l.Info("Reset token generated", zap.String("token", resetToken))

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"message": "Password reset link has been sent to your email",
		"token":   resetToken, // Только для тестирования, в production не возвращать!
	})
}

// ConfirmPasswordReset подтверждение сброса пароля
func (h *AuthHandler) ConfirmPasswordReset(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Info("Начало обработки подтверждения сброса пароля")

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

	if err := h.validator.Struct(req); err != nil {
		h.handleValidationError(w, err)
		return
	}

	if err := h.authService.ConfirmPasswordReset(req.Token, req.NewPassword); err != nil {
		l.Error("Ошибка подтверждения сброса пароля", zap.Error(err))

		status := http.StatusBadRequest
		message := "Password reset failed"

		if errors.Is(err, ErrResetTokenNotFound) || errors.Is(err, ErrResetTokenInvalid) {
			message = "Invalid or expired reset token"
		} else if errors.Is(err, ErrUserNotFound) {
			message = "User not found"
		}

		http.Error(w, message, status)
		return
	}

	l.Info("Пароль успешно изменен")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{
		"message": "Password has been successfully reset",
	})
}

// MobileLogin вход для мобильных приложений
func (h *AuthHandler) MobileLogin(w http.ResponseWriter, r *http.Request) {
	l := logger.Get()
	l.Debug("Начало обработки запроса входа (мобильный)")

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

	if err := h.validator.Struct(req); err != nil {
		h.handleValidationError(w, err)
		return
	}

	response, refreshToken, err := h.authService.Login(req)
	if err != nil {
		l.Error("Ошибка входа", zap.Error(err))

		status := http.StatusUnauthorized
		message := "Login failed"

		if errors.Is(err, ErrUserNotFound) {
			message = "User not found"
		} else if errors.Is(err, ErrInvalidPassword) {
			message = "Invalid password"
		}

		http.Error(w, message, status)
		return
	}

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"access_token":  response.Token,
		"refresh_token": refreshToken,
		"expires_at":    response.ExpiresAt,
		"user":          response.User,
	})
}

// handleValidationError обрабатывает ошибки валидации
func (h *AuthHandler) handleValidationError(w http.ResponseWriter, err error) {
	var invalidValidationError *validator.InvalidValidationError
	if errors.As(err, &invalidValidationError) {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	var errs []string
	for _, err := range err.(validator.ValidationErrors) {
		errs = append(errs, fmt.Sprintf("field %s is invalid: %s", err.Field(), err.Tag()))
	}

	w.WriteHeader(http.StatusBadRequest)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"error":  "Validation failed",
		"fields": errs,
	})
}