package handlers

import (
	"errors"

	"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
	jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
	"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
	"github.com/gofiber/fiber/v2"
)

// loginCustomerReq represents the request body for the LoginCustomer endpoint.
type loginCustomerReq struct {
	Email       string `json:"email" validate:"email" example:"john.doe@example.com"`
	PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
	Password    string `json:"password" validate:"required" example:"password123"`
}

// loginCustomerRes represents the response body for the LoginCustomer endpoint.
type loginCustomerRes struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token"`
	Role         string `json:"role"`
}

// LoginCustomer godoc
// @Summary Login customer
// @Description Login customer
// @Tags auth
// @Accept json
// @Produce json
// @Param login body loginCustomerReq true "Login customer"
// @Success 200 {object} loginCustomerRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /auth/login [post]
func (h *Handler) LoginCustomer(c *fiber.Ctx) error {

	var req loginCustomerReq
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("Failed to parse LoginCustomer request", "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}

	successRes, err := h.authSvc.Login(c.Context(), req.Email, req.PhoneNumber, req.Password)
	if err != nil {
		h.logger.Info("Login attempt failed", "email", req.Email, "phone", req.PhoneNumber, "error", err)
		switch {
		case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound):
			return fiber.NewError(fiber.StatusUnauthorized, "Invalid credentials")
		case errors.Is(err, authentication.ErrUserSuspended):
			return fiber.NewError(fiber.StatusUnauthorized, "User login has been locked")
		default:
			h.logger.Error("Login failed", "error", err)
			return fiber.NewError(fiber.StatusInternalServerError, "Internal server error")
		}
	}

	accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, successRes.CompanyID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
	if err != nil {
		h.logger.Error("Failed to create access token", "userID", successRes.UserId, "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate access token")
	}

	res := loginCustomerRes{
		AccessToken:  accessToken,
		RefreshToken: successRes.RfToken,
		Role:         string(successRes.Role),
	}
	return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil)
}

type refreshToken struct {
	AccessToken  string `json:"access_token" validate:"required" example:"<jwt-token>"`
	RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`
}

// RefreshToken godoc
// @Summary Refresh token
// @Description Refresh token
// @Tags auth
// @Accept json
// @Produce json
// @Param refresh body refreshToken true "tokens"
// @Success 200 {object} loginCustomerRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /auth/refresh [post]
func (h *Handler) RefreshToken(c *fiber.Ctx) error {

	type loginCustomerRes struct {
		AccessToken  string `json:"access_token"`
		RefreshToken string `json:"refresh_token"`
		Role         string `json:"role"`
	}

	var req refreshToken
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("Failed to parse RefreshToken request", "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}

	refreshToken, err := h.authSvc.RefreshToken(c.Context(), req.RefreshToken)
	if err != nil {
		h.logger.Info("Refresh token attempt failed", "refreshToken", req.RefreshToken, "error", err)
		switch {
		case errors.Is(err, authentication.ErrExpiredToken):
			return fiber.NewError(fiber.StatusUnauthorized, "The refresh token has expired")
		case errors.Is(err, authentication.ErrRefreshTokenNotFound):
			return fiber.NewError(fiber.StatusUnauthorized, "Refresh token not found")
		default:
			h.logger.Error("Refresh token failed", "error", err)
			return fiber.NewError(fiber.StatusInternalServerError, "Internal server error")
		}
	}

	user, err := h.userSvc.GetUserByID(c.Context(), refreshToken.UserID)

	// Assuming the refreshed token includes userID and role info; adjust if needed
	accessToken, err := jwtutil.CreateJwt(user.ID, user.Role, user.CompanyID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
	if err != nil {
		h.logger.Error("Failed to create new access token", "error", err)
		return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate access token")
	}

	res := loginCustomerRes{
		AccessToken:  accessToken,
		RefreshToken: req.RefreshToken,
		Role:         string(user.Role),
	}
	return response.WriteJSON(c, fiber.StatusOK, "Refresh successful", res, nil)
}

type logoutReq struct {
	RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`
}

// LogOutCustomer godoc
// @Summary Logout customer
// @Description Logout customer
// @Tags auth
// @Accept json
// @Produce json
// @Param logout body logoutReq true "Logout customer"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /auth/logout [post]
func (h *Handler) LogOutCustomer(c *fiber.Ctx) error {

	var req logoutReq
	if err := c.BodyParser(&req); err != nil {
		h.logger.Error("Failed to parse LogOutCustomer request", "error", err)
		return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
	}

	if valErrs, ok := h.validator.Validate(c, req); !ok {
		return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
	}

	err := h.authSvc.Logout(c.Context(), req.RefreshToken)
	if err != nil {
		h.logger.Info("Logout attempt failed", "refreshToken", req.RefreshToken, "error", err)
		switch {
		case errors.Is(err, authentication.ErrExpiredToken):
			return fiber.NewError(fiber.StatusUnauthorized, "The refresh token has expired")
		case errors.Is(err, authentication.ErrRefreshTokenNotFound):
			return fiber.NewError(fiber.StatusUnauthorized, "Refresh token not found")
		default:
			h.logger.Error("Logout failed", "error", err)
			return fiber.NewError(fiber.StatusInternalServerError, "Internal server error")
		}
	}

	return response.WriteJSON(c, fiber.StatusOK, "Logout successful", nil, nil)
}
