package handlers

import (
	"errors"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/tdvorak/seen/backend/internal/services/auth"
	"github.com/tdvorak/seen/backend/pkg/httpx"
)

type AuthHandler struct {
	service *auth.Service
}

func NewAuthHandler(service *auth.Service) *AuthHandler {
	return &AuthHandler{service: service}
}

type registerRequest struct {
	Email       string `json:"email"`
	Password    string `json:"password"`
	DisplayName string `json:"displayName"`
}

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

type refreshRequest struct {
	RefreshToken string `json:"refreshToken"`
}

func (h *AuthHandler) Me(c *gin.Context) {
	accessToken, ok := bearerToken(c.GetHeader("Authorization"))
	if !ok {
		httpx.JSONError(c, http.StatusUnauthorized, "missing bearer token")
		return
	}

	user, err := h.service.UserFromAccessToken(c.Request.Context(), accessToken)
	if err != nil {
		switch {
		case errors.Is(err, auth.ErrInvalidToken):
			httpx.JSONError(c, http.StatusUnauthorized, err.Error())
		default:
			httpx.JSONError(c, http.StatusInternalServerError, "failed to resolve user")
		}
		return
	}

	httpx.JSON(c, http.StatusOK, user)
}

func (h *AuthHandler) Register(c *gin.Context) {
	var request registerRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
		return
	}

	result, err := h.service.Register(c.Request.Context(), auth.RegisterInput{
		Email:       request.Email,
		Password:    request.Password,
		DisplayName: request.DisplayName,
	})
	if err != nil {
		switch {
		case errors.Is(err, auth.ErrInvalidInput):
			httpx.JSONError(c, http.StatusBadRequest, err.Error())
		case errors.Is(err, auth.ErrEmailTaken):
			httpx.JSONError(c, http.StatusConflict, err.Error())
		default:
			httpx.JSONError(c, http.StatusInternalServerError, "register failed")
		}
		return
	}

	httpx.JSON(c, http.StatusCreated, result)
}

func (h *AuthHandler) Login(c *gin.Context) {
	var request loginRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
		return
	}

	result, err := h.service.Login(c.Request.Context(), auth.LoginInput{
		Email:     request.Email,
		Password:  request.Password,
		UserAgent: c.GetHeader("User-Agent"),
		IP:        c.ClientIP(),
	})
	if err != nil {
		switch {
		case errors.Is(err, auth.ErrInvalidInput):
			httpx.JSONError(c, http.StatusBadRequest, err.Error())
		case errors.Is(err, auth.ErrInvalidCredentials):
			httpx.JSONError(c, http.StatusUnauthorized, err.Error())
		default:
			httpx.JSONError(c, http.StatusInternalServerError, "login failed")
		}
		return
	}

	httpx.JSON(c, http.StatusOK, result)
}

func (h *AuthHandler) Refresh(c *gin.Context) {
	var request refreshRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		httpx.JSONError(c, http.StatusBadRequest, "invalid request body")
		return
	}

	result, err := h.service.Refresh(c.Request.Context(), auth.RefreshInput{
		RefreshToken: request.RefreshToken,
		UserAgent:    c.GetHeader("User-Agent"),
		IP:           c.ClientIP(),
	})
	if err != nil {
		switch {
		case errors.Is(err, auth.ErrInvalidInput):
			httpx.JSONError(c, http.StatusBadRequest, err.Error())
		case errors.Is(err, auth.ErrInvalidSession):
			httpx.JSONError(c, http.StatusUnauthorized, err.Error())
		default:
			httpx.JSONError(c, http.StatusInternalServerError, "refresh failed")
		}
		return
	}

	httpx.JSON(c, http.StatusOK, result)
}
