package handlers

import (
	"errors"
	"net/http"

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

	"git.threesix.ai/jordan/slack5-1770541397/pkg/app"
	"git.threesix.ai/jordan/slack5-1770541397/pkg/auth"
	"git.threesix.ai/jordan/slack5-1770541397/pkg/httperror"
	"git.threesix.ai/jordan/slack5-1770541397/pkg/httpresponse"
	"git.threesix.ai/jordan/slack5-1770541397/pkg/logging"
	"git.threesix.ai/jordan/slack5-1770541397/services/preferences-api/internal/domain"
	"git.threesix.ai/jordan/slack5-1770541397/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"),
	}
}

// PutPreferencesRequest is the request body for setting preferences.
type PutPreferencesRequest struct {
	Preferences PreferencesPayload `json:"preferences" validate:"required"`
}

// PreferencesPayload represents the preferences JSON structure in requests/responses.
type PreferencesPayload struct {
	Theme         string                          `json:"theme,omitempty"`
	Language      string                          `json:"language,omitempty"`
	Notifications *NotificationPreferencesPayload `json:"notifications,omitempty"`
}

// NotificationPreferencesPayload represents notification settings in requests/responses.
type NotificationPreferencesPayload struct {
	Email bool `json:"email"`
	Push  bool `json:"push"`
	SMS   bool `json:"sms"`
}

// PreferencesResponse is the response for a user's preferences.
type PreferencesResponse struct {
	UserID      string             `json:"user_id"`
	Preferences PreferencesPayload `json:"preferences"`
	UpdatedAt   string             `json:"updated_at"`
}

// toPreferencesResponse converts a domain UserPreferences to an API response.
func toPreferencesResponse(up *domain.UserPreferences) PreferencesResponse {
	return PreferencesResponse{
		UserID: up.UserID.String(),
		Preferences: PreferencesPayload{
			Theme:    up.Preferences.Theme,
			Language: up.Preferences.Language,
			Notifications: &NotificationPreferencesPayload{
				Email: up.Preferences.Notifications.Email,
				Push:  up.Preferences.Notifications.Push,
				SMS:   up.Preferences.Notifications.SMS,
			},
		},
		UpdatedAt: up.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}
}

// toDomainPreferences converts an API payload to a domain Preferences.
func toDomainPreferences(p PreferencesPayload) domain.Preferences {
	prefs := domain.Preferences{
		Theme:    p.Theme,
		Language: p.Language,
	}
	if p.Notifications != nil {
		prefs.Notifications = domain.NotificationPreferences{
			Email: p.Notifications.Email,
			Push:  p.Notifications.Push,
			SMS:   p.Notifications.SMS,
		}
	}
	return prefs
}

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

	// Authorization: authenticated user must match path user_id
	authUser := auth.GetUser(r.Context())
	if authUser == nil || authUser.ID != userID {
		return httperror.Forbidden("access denied: can only access own preferences")
	}

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

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

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

	// Authorization: authenticated user must match path user_id
	authUser := auth.GetUser(r.Context())
	if authUser == nil || authUser.ID != userID {
		return httperror.Forbidden("access denied: can only modify own preferences")
	}

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

	prefs, err := h.svc.SetPreferences(r.Context(), domain.UserID(userID), toDomainPreferences(req.Preferences))
	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("invalid theme: must be one of light, dark, system")
	case errors.Is(err, domain.ErrInvalidLanguage):
		return httperror.BadRequest("invalid language: must be at most 10 characters")
	default:
		return err
	}
}
