package handlers

import (
	"errors"
	"net/http"

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

	"git.threesix.ai/jordan/slack5-1770603014/pkg/app"
	"git.threesix.ai/jordan/slack5-1770603014/pkg/httperror"
	"git.threesix.ai/jordan/slack5-1770603014/pkg/httpresponse"
	"git.threesix.ai/jordan/slack5-1770603014/pkg/logging"
	"git.threesix.ai/jordan/slack5-1770603014/services/preferences-api/internal/domain"
	"git.threesix.ai/jordan/slack5-1770603014/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 PreferencesInput `json:"preferences" validate:"required"`
}

// PreferencesInput uses pointers to distinguish "not provided" from zero values.
type PreferencesInput struct {
	Theme         *string            `json:"theme,omitempty"`
	Language      *string            `json:"language,omitempty"`
	Notifications *NotificationsInput `json:"notifications,omitempty"`
}

// NotificationsInput uses pointers for partial update semantics.
type NotificationsInput struct {
	Email  *bool   `json:"email,omitempty"`
	Push   *bool   `json:"push,omitempty"`
	Digest *string `json:"digest,omitempty"`
}

// PreferencesResponse is the response for preferences endpoints.
type PreferencesResponse struct {
	UserID      string             `json:"user_id"`
	Preferences domain.Preferences `json:"preferences"`
	UpdatedAt   string             `json:"updated_at"`
}

// toPreferencesResponse converts a domain UserPreferences to an API response.
func toPreferencesResponse(p *domain.UserPreferences) PreferencesResponse {
	return PreferencesResponse{
		UserID:      p.UserID.String(),
		Preferences: p.Preferences,
		UpdatedAt:   p.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}
}

// 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")
	}

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

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

// Upsert creates or updates preferences for a user.
func (h *Preferences) Upsert(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
	}

	input := service.UpdateInput{
		Theme:    req.Preferences.Theme,
		Language: req.Preferences.Language,
	}
	if req.Preferences.Notifications != nil {
		input.Notifications = &service.NotificationsInput{
			Email:  req.Preferences.Notifications.Email,
			Push:   req.Preferences.Notifications.Push,
			Digest: req.Preferences.Notifications.Digest,
		}
	}

	prefs, err := h.svc.UpdatePreferences(r.Context(), domain.UserID(userID), input)
	if err != nil {
		return mapPreferencesDomainError(err)
	}

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

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