package users

import (
	"encoding/json"
	"errors"
	"log/slog"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
	"github.com/ultisuite/ulti-backend/internal/api/middleware"
	"github.com/ultisuite/ulti-backend/internal/config"
	"github.com/ultisuite/ulti-backend/internal/permission"
	platformusers "github.com/ultisuite/ulti-backend/internal/users"
	"github.com/ultisuite/ulti-backend/internal/orgpolicy"
)

type Handler struct {
	db        *pgxpool.Pool
	cfg       *config.Config
	logger    *slog.Logger
	orgPolicy *orgpolicy.Loader
}

func NewHandler(db *pgxpool.Pool, cfg *config.Config) *Handler {
	return &Handler{
		db:        db,
		cfg:       cfg,
		orgPolicy: orgpolicy.NewLoader(db, nil),
		logger:    slog.Default().With("component", "users-api"),
	}
}

func (h *Handler) Routes() chi.Router {
	r := chi.NewRouter()
	r.Get("/me", h.Me)
	r.Put("/me/avatar", h.PutAvatar)
	r.Delete("/me/avatar", h.DeleteAvatar)
	return r
}

func (h *Handler) Me(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())
	if claims == nil {
		apiresponse.WriteError(w, r, http.StatusUnauthorized, apiresponse.CodeAuthUnauthorized, "unauthorized", nil)
		return
	}

	state, err := platformusers.GetAccountState(r.Context(), h.db, claims.Sub)
	if err != nil {
		h.logger.Error("read account state", "error", err)
		apivalidate.WriteInternal(w, r)
		return
	}
	role := permission.DeriveAccountRole(state.PlatformAdmin, state.Status)

	orgAgenda, err := h.orgPolicy.PublicAgendaPolicy(r.Context())
	if err != nil {
		h.logger.Warn("read org agenda policy", "error", err)
		orgAgenda = orgpolicy.PublicAgendaPolicy{
			DefaultThemeMode:         "system",
			DefaultVideoProvider:     "ultimeet",
			ConfiguredVideoProviders: []string{"ultimeet"},
		}
	}

	orgDrive, err := h.orgPolicy.PublicDrivePolicy(r.Context())
	if err != nil {
		h.logger.Warn("read org drive policy", "error", err)
		orgDrive = orgpolicy.PublicDrivePolicy{}
	}

	avatarURL, err := platformusers.GetAvatarURL(r.Context(), h.db, claims.Sub)
	if err != nil {
		h.logger.Warn("read user avatar", "error", err)
	}
	if avatarURL == "" {
		if imported, importErr := platformusers.ImportAvatarFromAuthentik(r.Context(), h.db, h.cfg, claims.Sub); importErr != nil {
			h.logger.Warn("import avatar from authentik", "error", importErr)
		} else if imported != "" {
			avatarURL = imported
		}
	}

	payload := map[string]any{
		"sub":            claims.Sub,
		"email":          claims.Email,
		"name":           claims.Name,
		"status":         state.Status,
		"platform_admin": state.PlatformAdmin,
		"role":           role,
		"groups":         claims.Groups,
		"org_agenda":     orgAgenda,
		"org_drive":      orgDrive,
	}
	if avatarURL != "" {
		payload["avatar_url"] = avatarURL
	}

	apiresponse.WriteJSON(w, http.StatusOK, payload)
}

func (h *Handler) PutAvatar(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())
	if claims == nil {
		apiresponse.WriteError(w, r, http.StatusUnauthorized, apiresponse.CodeAuthUnauthorized, "unauthorized", nil)
		return
	}

	var body struct {
		AvatarURL string `json:"avatar_url"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid json body", nil)
		return
	}

	if err := platformusers.SetAvatarURL(r.Context(), h.db, claims.Sub, body.AvatarURL); err != nil {
		switch {
		case errors.Is(err, platformusers.ErrAvatarTooLarge):
			apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "avatar too large (max 512 KiB)", nil)
		case errors.Is(err, platformusers.ErrAvatarInvalid):
			apiresponse.WriteError(w, r, http.StatusBadRequest, apiresponse.CodeInvalidRequest, "invalid avatar image", nil)
		case errors.Is(err, pgx.ErrNoRows):
			apivalidate.WriteNotFound(w, r, "user not found")
		default:
			h.logger.Error("set user avatar", "error", err)
			apivalidate.WriteInternal(w, r)
		}
		return
	}

	platformusers.PropagateAvatarOutbound(r.Context(), h.db, h.cfg, claims, body.AvatarURL)

	apiresponse.WriteJSON(w, http.StatusOK, map[string]any{
		"avatar_url": body.AvatarURL,
	})
}

func (h *Handler) DeleteAvatar(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())
	if claims == nil {
		apiresponse.WriteError(w, r, http.StatusUnauthorized, apiresponse.CodeAuthUnauthorized, "unauthorized", nil)
		return
	}

	if err := platformusers.ClearAvatarURL(r.Context(), h.db, claims.Sub); err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			apivalidate.WriteNotFound(w, r, "user not found")
			return
		}
		h.logger.Error("clear user avatar", "error", err)
		apivalidate.WriteInternal(w, r)
		return
	}

	platformusers.PropagateAvatarOutbound(r.Context(), h.db, h.cfg, claims, "")

	apiresponse.WriteJSON(w, http.StatusOK, map[string]any{"ok": true})
}
