package handlers

import (
	"errors"
	"net/http"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"

	"git.threesix.ai/jordan/slack5-1770544098/pkg/app"
	"git.threesix.ai/jordan/slack5-1770544098/pkg/auth"
	"git.threesix.ai/jordan/slack5-1770544098/pkg/httperror"
	"git.threesix.ai/jordan/slack5-1770544098/pkg/httpresponse"
	"git.threesix.ai/jordan/slack5-1770544098/pkg/logging"
	"git.threesix.ai/jordan/slack5-1770544098/services/preferences-api/internal/domain"
	"git.threesix.ai/jordan/slack5-1770544098/services/preferences-api/internal/service"
)

// Preferences handles HTTP requests for user preferences.
type Preferences struct {
	svc    *service.PreferencesService
	logger *logging.Logger
}

// NewPreferences creates a new Preferences handler with injected dependencies.
func NewPreferences(svc *service.PreferencesService, logger *logging.Logger) *Preferences {
	return &Preferences{
		svc:    svc,
		logger: logger.WithComponent("PreferencesHandler"),
	}
}

// UpdatePreferencesRequest is the request body for updating preferences.
type UpdatePreferencesRequest struct {
	Preferences map[string]any `json:"preferences" validate:"required"`
}

// PreferencesResponse is the response DTO for user preferences.
type PreferencesResponse struct {
	UserID      string         `json:"user_id"`
	Preferences map[string]any `json:"preferences"`
	UpdatedAt   *time.Time     `json:"updated_at"`
}

// Get returns preferences for a user.
func (h *Preferences) Get(w http.ResponseWriter, r *http.Request) error {
	userID := chi.URLParam(r, "user_id")

	if _, err := uuid.Parse(userID); err != nil {
		return httperror.BadRequest("invalid user ID format")
	}

	if err := h.checkOwnership(r, userID); err != nil {
		return err
	}

	prefs, err := h.svc.Get(r.Context(), userID)
	if err != nil {
		return err
	}

	httpresponse.OK(w, r, toPreferencesResponse(prefs))
	return nil
}

// Update creates or updates preferences for a user.
func (h *Preferences) Update(w http.ResponseWriter, r *http.Request) error {
	userID := chi.URLParam(r, "user_id")

	if _, err := uuid.Parse(userID); err != nil {
		return httperror.BadRequest("invalid user ID format")
	}

	var req UpdatePreferencesRequest
	if err := app.BindAndValidate(r, &req); err != nil {
		return err
	}

	if err := h.checkOwnership(r, userID); err != nil {
		return err
	}

	prefs, err := h.svc.Update(r.Context(), userID, req.Preferences)
	if err != nil {
		return mapPreferencesDomainError(err)
	}

	httpresponse.OK(w, r, toPreferencesResponse(prefs))
	return nil
}

// checkOwnership verifies the authenticated user owns the requested resource.
func (h *Preferences) checkOwnership(r *http.Request, userID string) error {
	user := auth.MustGetUser(r.Context())
	if user.ID != userID {
		return httperror.Forbidden("access denied")
	}
	return nil
}

// toPreferencesResponse converts a domain UserPreferences to an API response.
func toPreferencesResponse(p *domain.UserPreferences) PreferencesResponse {
	resp := PreferencesResponse{
		UserID:      p.UserID,
		Preferences: p.Preferences,
	}
	if !p.UpdatedAt.IsZero() {
		t := p.UpdatedAt
		resp.UpdatedAt = &t
	}
	return resp
}

// mapPreferencesDomainError converts domain errors to HTTP errors.
func mapPreferencesDomainError(err error) error {
	switch {
	case errors.Is(err, domain.ErrInvalidPreferenceKey):
		return httperror.BadRequest(err.Error())
	case errors.Is(err, domain.ErrInvalidPreferenceValue):
		return httperror.BadRequest(err.Error())
	default:
		return err
	}
}
