package ultidraw

import (
	"io"
	"net/http"
	"strings"

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

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
)

type publicSessionRequest struct {
	Path        string `json:"path"`
	Mode        string `json:"mode"`
	Password    string `json:"password"`
	GuestID     string `json:"guest_id"`
	GuestName   string `json:"guest_name"`
	DisplayName string `json:"display_name"`
}

func (h *Handler) PublicShareSession(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	if token == "" {
		apivalidate.WriteNotFound(w, r, "not found")
		return
	}
	var req publicSessionRequest
	if err := apivalidate.DecodeJSON(w, r, 32<<10, &req); err != nil {
		return
	}
	if strings.TrimSpace(req.Path) == "" {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "path", Message: "required"},
		))
		return
	}
	password := strings.TrimSpace(req.Password)
	perms, err := h.svc.EffectivePublicSharePermissions(r.Context(), token, req.Path, password)
	if err != nil {
		apivalidate.WriteInternal(w, r)
		return
	}
	if !nextcloud.PublicShareCanRead(perms) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}
	mode := strings.TrimSpace(req.Mode)
	if mode == "" {
		mode = "edit"
	}
	if mode == "edit" && !nextcloud.PublicShareCanUpdate(perms) {
		mode = "view"
	}

	guestID := strings.TrimSpace(req.GuestID)
	if guestID == "" {
		guestID = "public-guest"
	} else {
		guestID = "public:" + guestID
	}
	guestName := strings.TrimSpace(req.GuestName)
	if guestName == "" {
		guestName = "Invité"
	}

	result, err := h.svc.CreatePublicSession(r.Context(), token, req.Path, mode, password, guestID, guestName, strings.TrimSpace(req.DisplayName))
	if err != nil {
		h.logger.Error("public ultidraw session", "error", err)
		apivalidate.WriteInternal(w, r)
		return
	}
	result.Mode = mode
	apiresponse.WriteJSON(w, http.StatusOK, result)
}

func (h *Handler) PublicShareDocument(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	path := strings.TrimSpace(r.URL.Query().Get("path"))
	password := strings.TrimSpace(r.URL.Query().Get("password"))
	sig := strings.TrimSpace(r.URL.Query().Get("sig"))
	if h.svc.Cfg.HocuspocusSecret != "" && !verifyPublicDocAccess(token, path, password, sig, h.svc.Cfg.HocuspocusSecret) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}
	body, err := h.svc.LoadPublicDocumentLegacy(r.Context(), token, path, password)
	if err != nil {
		http.Error(w, "not found", http.StatusNotFound)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	_, _ = w.Write(body)
}

func (h *Handler) PublicSharePutDocument(w http.ResponseWriter, r *http.Request) {
	token := strings.TrimSpace(chi.URLParam(r, "token"))
	path := strings.TrimSpace(r.URL.Query().Get("path"))
	password := strings.TrimSpace(r.URL.Query().Get("password"))
	sig := strings.TrimSpace(r.URL.Query().Get("sig"))
	if h.svc.Cfg.HocuspocusSecret != "" && !verifyPublicDocAccess(token, path, password, sig, h.svc.Cfg.HocuspocusSecret) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}
	perms, err := h.svc.EffectivePublicSharePermissions(r.Context(), token, path, password)
	if err != nil || !nextcloud.PublicShareCanUpdate(perms) {
		http.Error(w, "forbidden", http.StatusForbidden)
		return
	}
	raw, err := io.ReadAll(r.Body)
	if err != nil {
		apivalidate.WriteInternal(w, r)
		return
	}
	existingRaw, loadErr := h.svc.LoadPublicDocumentLegacy(r.Context(), token, path, password)
	var existing UltiDrawDoc
	if loadErr == nil && len(existingRaw) > 0 {
		if parsed, parseErr := ParseUltiDrawDoc(existingRaw); parseErr == nil {
			existing = parsed
		}
	}
	doc, err := ApplyUltiDrawPatch(existing, raw)
	if err != nil {
		apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
			apivalidate.FieldDetail{Field: "document", Message: "invalid JSON"},
		))
		return
	}
	payload, err := doc.Marshal()
	if err != nil {
		apivalidate.WriteInternal(w, r)
		return
	}
	if err := h.svc.SavePublicDocumentLegacy(r.Context(), token, path, password, payload); err != nil {
		http.Error(w, "save failed", http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
