package auth

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

	"gitea.local/admin/hspguard/internal/repository"
	"gitea.local/admin/hspguard/internal/types"
	"gitea.local/admin/hspguard/internal/util"
	"gitea.local/admin/hspguard/internal/web"
	"github.com/go-chi/chi/v5"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

type AuthHandler struct {
	repo *repository.Queries
}

func NewAuthHandler(repo *repository.Queries) *AuthHandler {
	return &AuthHandler{
		repo: repo,
	}
}

func (h *AuthHandler) RegisterRoutes(api chi.Router) {
	api.Get("/profile", h.getProfile)
	api.Post("/login", h.login)
}

func (h *AuthHandler) getProfile(w http.ResponseWriter, r *http.Request) {
	userId, ok := util.GetRequestUserId(r.Context())
	if !ok {
		web.Error(w, "failed to get user id from auth session", http.StatusInternalServerError)
		return
	}

	user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(userId))
	if err != nil {
		web.Error(w, "user with provided id does not exist", http.StatusUnauthorized)
		return
	}

	if err := json.NewEncoder(w).Encode(map[string]any{
		"full_name":       user.FullName,
		"email":           user.Email,
		"phoneNumber":     user.PhoneNumber,
		"isAdmin":         user.IsAdmin,
		"last_login":      user.LastLogin,
		"profile_picture": user.ProfilePicture.String,
		"updated_at":      user.UpdatedAt,
		"created_at":      user.CreatedAt,
	}); err != nil {
		web.Error(w, "failed to encode user profile", http.StatusInternalServerError)
	}
}

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

func (h *AuthHandler) login(w http.ResponseWriter, r *http.Request) {
	var params LoginParams

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&params); err != nil {
		web.Error(w, "failed to parse request body", http.StatusBadRequest)
		return
	}

	if params.Email == "" || params.Password == "" {
		web.Error(w, "missing required fields", http.StatusBadRequest)
		return
	}

	user, err := h.repo.FindUserEmail(r.Context(), params.Email)
	if err != nil {
		web.Error(w, "user with provided email does not exists", http.StatusBadRequest)
		return
	}

	if !util.VerifyPassword(params.Password, user.PasswordHash) {
		web.Error(w, "username or/and password are incorrect", http.StatusBadRequest)
		return
	}

	accessClaims := types.UserClaims{
		UserEmail: user.Email,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    "hspguard",
			Subject:   user.ID.String(),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
		},
	}

	accessToken, err := SignJwtToken(accessClaims)
	if err != nil {
		web.Error(w, fmt.Sprintf("failed to generate access token: %v", err), http.StatusBadRequest)
		return
	}

	refreshClaims := types.UserClaims{
		UserEmail: user.Email,
		RegisteredClaims: jwt.RegisteredClaims{
			Issuer:    "hspguard",
			Subject:   user.ID.String(),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * 24 * time.Hour)),
		},
	}

	refreshToken, err := SignJwtToken(refreshClaims)
	if err != nil {
		web.Error(w, fmt.Sprintf("failed to generate refresh token: %v", err), http.StatusBadRequest)
		return
	}

	encoder := json.NewEncoder(w)

	type Response struct {
		AccessToken  string `json:"access"`
		RefreshToken string `json:"refresh"`
		// fields required for UI in account selector, e.g. email, full name and avatar
		FullName       string `json:"full_name"`
		Email          string `json:"email"`
		Id             string `json:"id"`
		ProfilePicture string `json:"profile_picture"`
		// Avatar
	}

	w.Header().Set("Content-Type", "application/json")

	if err := encoder.Encode(Response{
		AccessToken:    accessToken,
		RefreshToken:   refreshToken,
		FullName:       user.FullName,
		Email:          user.Email,
		Id:             user.ID.String(),
		ProfilePicture: user.ProfilePicture.String,
		// Avatar
	}); err != nil {
		web.Error(w, "failed to encode response", http.StatusInternalServerError)
	}
}
