package meet

import (
	"log/slog"
	"net/http"
	"strings"

	"github.com/go-chi/chi/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/auth"
	meetpkg "github.com/ultisuite/ulti-backend/internal/meet"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
	"github.com/ultisuite/ulti-backend/internal/orgpolicy"
)

type Handler struct {
	svc                *Service
	enabled            bool
	publicURL          string
	policy             *orgpolicy.Loader
	transcripts        *TranscriptProcessor
	transcriptSecret   string
	logger             *slog.Logger
}

func NewHandler(
	meetCfg *meetpkg.Config,
	enabled bool,
	publicURL string,
	policy *orgpolicy.Loader,
	db *pgxpool.Pool,
	nc *nextcloud.Client,
	transcriptSecret string,
) *Handler {
	return &Handler{
		svc:              NewService(meetCfg, policy),
		enabled:          enabled && meetCfg != nil,
		publicURL:        strings.TrimRight(strings.TrimSpace(publicURL), "/"),
		policy:           policy,
		transcripts:      NewTranscriptProcessor(db, nc, policy),
		transcriptSecret: strings.TrimSpace(transcriptSecret),
		logger:           slog.Default().With("component", "meet-api"),
	}
}

func (h *Handler) Routes() chi.Router {
	r := chi.NewRouter()
	r.Get("/config", h.GetConfig)
	r.Post("/transcripts", h.ReceiveTranscript)
	r.Group(func(r chi.Router) {
		r.Use(middleware.RequireFullAccount)
		r.Post("/rooms", h.CreateRoom)
		r.Post("/rooms/{roomID}/token", h.GetToken)
	})
	return r
}

func (h *Handler) GetConfig(w http.ResponseWriter, r *http.Request) {
	resp := map[string]any{
		"enabled":    h.enabled,
		"public_url": h.publicURL,
		"brand_name": "UltiMeet",
	}
	if h.policy != nil {
		if pub, err := h.policy.PublicMeetPolicy(r.Context()); err == nil {
			resp["transcription_enabled"] = pub.TranscriptionEnabled
			resp["transcription_mode"] = pub.TranscriptionMode
			resp["auto_start_transcription"] = pub.AutoStartTranscription
		}
	}
	apiresponse.WriteJSON(w, http.StatusOK, resp)
}

func meetUser(claims *auth.Claims) *meetpkg.UserInfo {
	return &meetpkg.UserInfo{
		ID:    claims.Sub,
		Name:  claims.Name,
		Email: claims.Email,
	}
}

func (h *Handler) CreateRoom(w http.ResponseWriter, r *http.Request) {
	if !h.enabled {
		apiresponse.WriteError(w, r, http.StatusConflict, "meet_disabled", "meet is disabled", nil)
		return
	}
	claims := middleware.ClaimsFromContext(r.Context())

	var req createRoomRequest
	if r.ContentLength != 0 {
		if err := apivalidate.DecodeJSON(w, r, maxRequestBody, &req); err != nil {
			return
		}
	}
	if verr := validateCreateRoom(&req); verr != nil {
		apivalidate.WriteValidationError(w, r, verr)
		return
	}

	user := meetUser(claims)
	user.IsMod = true
	token, err := h.svc.CreateRoom(r.Context(), req.Name, user)
	if err != nil {
		h.logger.Error("create room token", "error", err)
		apivalidate.WriteInternal(w, r)
		return
	}
	apiresponse.WriteJSON(w, http.StatusCreated, token)
}

func (h *Handler) GetToken(w http.ResponseWriter, r *http.Request) {
	if !h.enabled {
		apiresponse.WriteError(w, r, http.StatusConflict, "meet_disabled", "meet is disabled", nil)
		return
	}
	claims := middleware.ClaimsFromContext(r.Context())
	roomID := chi.URLParam(r, "roomID")
	if verr := validateRoomID(roomID); verr != nil {
		apivalidate.WriteValidationError(w, r, verr)
		return
	}

	user := meetUser(claims)
	user.IsMod = false

	token, err := h.svc.GetToken(r.Context(), roomID, user)
	if err != nil {
		h.logger.Error("get room token", "error", err)
		apivalidate.WriteInternal(w, r)
		return
	}
	apiresponse.WriteJSON(w, http.StatusOK, token)
}

func (h *Handler) ReceiveTranscript(w http.ResponseWriter, r *http.Request) {
	if h.transcriptSecret != "" {
		authHeader := strings.TrimSpace(r.Header.Get("Authorization"))
		if authHeader != "Bearer "+h.transcriptSecret {
			apiresponse.WriteError(w, r, http.StatusUnauthorized, "auth.unauthorized", "unauthorized", nil)
			return
		}
	}

	var req transcriptRequest
	if err := apivalidate.DecodeJSON(w, r, 2<<20, &req); err != nil {
		return
	}
	if verr := validateTranscriptRequest(&req); verr != nil {
		apivalidate.WriteValidationError(w, r, verr)
		return
	}

	claims := middleware.ClaimsFromContext(r.Context())
	organizerUserID := strings.TrimSpace(req.OrganizerUserID)
	organizerEmail := strings.TrimSpace(req.OrganizerEmail)
	if claims != nil {
		if organizerUserID == "" {
			organizerUserID = claims.Sub
		}
		if organizerEmail == "" {
			organizerEmail = claims.Email
		}
	}

	err := h.transcripts.Handle(r.Context(), transcriptJobInput{
		RoomID:            strings.TrimSpace(req.RoomID),
		OrganizerUserID:   organizerUserID,
		OrganizerEmail:    organizerEmail,
		ParticipantEmails: req.ParticipantEmails,
		RawTranscript:     req.Transcript,
		Mode:              strings.TrimSpace(req.Mode),
		QueuedAudioURL:    strings.TrimSpace(req.QueuedAudioURL),
	})
	if err != nil {
		h.logger.Error("process transcript", "error", err)
		apiresponse.WriteError(w, r, http.StatusBadRequest, "transcript_failed", err.Error(), nil)
		return
	}
	apiresponse.WriteJSON(w, http.StatusAccepted, map[string]any{"ok": true})
}
