package handlers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"mgit.msbls.de/m/paliad/internal/auth"
	"mgit.msbls.de/m/paliad/internal/checklists"
)

type ChecklistFeedback struct {
	FeedbackType string `json:"feedback_type"`
	Checklist    string `json:"checklist"`
	Message      string `json:"message"`
}

func handleChecklistsPage(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "dist/checklists.html")
}

// handleChecklistsAuthorPage serves the authoring wizard (new + edit
// share the same bundle; the client reads location.pathname to decide
// create vs edit mode).
func handleChecklistsAuthorPage(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "dist/checklists-author.html")
}

func handleChecklistDetailPage(w http.ResponseWriter, r *http.Request) {
	slug := r.PathValue("slug")
	// Static catalog match → serve unconditionally; the static templates
	// are always visible.
	if _, ok := checklists.Find(slug); ok {
		http.ServeFile(w, r, "dist/checklists-detail.html")
		return
	}
	// Otherwise fall back to the DB-backed catalog (authored templates,
	// slug shape "u-a-..." from t-paliad-225). The catalog enforces
	// visibility per-user; a slug the caller can't see returns
	// ErrNotVisible and the user gets the same 404 they'd see for an
	// unknown slug. Without this branch authored checklists 404'd at the
	// page level even though they showed up in the overview, which is
	// exactly m's 2026-05-22 report.
	if dbSvc != nil && dbSvc.checklistCatalog != nil {
		uid, ok := auth.UserIDFromContext(r.Context())
		if ok {
			if _, err := dbSvc.checklistCatalog.Find(r.Context(), uid, slug); err == nil {
				http.ServeFile(w, r, "dist/checklists-detail.html")
				return
			}
		}
	}
	http.NotFound(w, r)
}

func handleChecklistInstancePage(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "dist/checklists-instance.html")
}

// handleChecklistsAPI returns the merged catalog: static templates
// (always) plus authored DB templates the caller can see (mig 114).
// Each entry carries origin + visibility + author metadata so the
// frontend can render provenance.
//
// Falls back to the bare static catalog when DB is unavailable so the
// knowledge-platform-only deploy stays functional without DATABASE_URL.
func handleChecklistsAPI(w http.ResponseWriter, r *http.Request) {
	if dbSvc == nil || dbSvc.checklistCatalog == nil {
		// Fall back to static summaries shape so the existing frontend
		// keeps working in the no-DB deploy.
		writeJSON(w, http.StatusOK, checklists.Summaries())
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	entries, err := dbSvc.checklistCatalog.ListVisible(r.Context(), uid)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	// Frontend expects the existing Summary shape on the index list; map
	// the merged entries to Summary + origin/visibility/author fields.
	type Summary struct {
		checklists.Summary
		Origin           string `json:"origin"`
		Visibility       string `json:"visibility"`
		OwnerEmail       string `json:"owner_email,omitempty"`
		OwnerDisplayName string `json:"owner_display_name,omitempty"`
	}
	out := make([]Summary, 0, len(entries))
	for _, e := range entries {
		out = append(out, Summary{
			Summary: checklists.Summary{
				Slug:          e.Template.Slug,
				TitleDE:       e.Template.TitleDE,
				TitleEN:       e.Template.TitleEN,
				DescriptionDE: e.Template.DescriptionDE,
				DescriptionEN: e.Template.DescriptionEN,
				Regime:        e.Template.Regime,
				CourtDE:       e.Template.CourtDE,
				CourtEN:       e.Template.CourtEN,
				ItemCount:     checklists.TotalItems(e.Template),
			},
			Origin:           e.Origin,
			Visibility:       e.Visibility,
			OwnerEmail:       e.OwnerEmail,
			OwnerDisplayName: e.OwnerDisplayName,
		})
	}
	writeJSON(w, http.StatusOK, out)
}

// handleChecklistAPI returns one template by slug. Looks up static
// catalog first (always visible), then authored DB rows via the
// catalog with visibility check.
func handleChecklistAPI(w http.ResponseWriter, r *http.Request) {
	slug := r.PathValue("slug")
	// Static-first path keeps the no-DB deploy functional and is the
	// common case for the curated templates.
	if c, ok := checklists.Find(slug); ok {
		writeJSON(w, http.StatusOK, c)
		return
	}
	if dbSvc == nil || dbSvc.checklistCatalog == nil {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": "Checkliste nicht gefunden."})
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	entry, err := dbSvc.checklistCatalog.Find(r.Context(), uid, slug)
	if err != nil {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": "Checkliste nicht gefunden."})
		return
	}
	// Re-render as the bilingual Template shape plus a thin meta block.
	// Version is included so the instance detail page can decide whether
	// to show the "template updated since this instance was created"
	// badge (Slice C).
	type templateWithMeta struct {
		checklists.Template
		Origin           string `json:"origin"`
		Visibility       string `json:"visibility"`
		OwnerEmail       string `json:"owner_email,omitempty"`
		OwnerDisplayName string `json:"owner_display_name,omitempty"`
		Version          int    `json:"version"`
	}
	writeJSON(w, http.StatusOK, templateWithMeta{
		Template:         entry.Template,
		Origin:           entry.Origin,
		Visibility:       entry.Visibility,
		OwnerEmail:       entry.OwnerEmail,
		OwnerDisplayName: entry.OwnerDisplayName,
		Version:          entry.Version,
	})
}

func handleChecklistsFeedback(w http.ResponseWriter, r *http.Request) {
	var feedback ChecklistFeedback
	if err := json.NewDecoder(r.Body).Decode(&feedback); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
		return
	}

	feedback.FeedbackType = strings.TrimSpace(feedback.FeedbackType)
	feedback.Checklist = strings.TrimSpace(feedback.Checklist)
	feedback.Message = strings.TrimSpace(feedback.Message)

	if feedback.Message == "" || feedback.FeedbackType == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Nachricht und Art sind erforderlich."})
		return
	}

	accessToken := ""
	email := ""
	if cookie, err := r.Cookie(auth.SessionCookieName); err == nil {
		accessToken = cookie.Value
		email = extractEmailFromJWT(cookie.Value)
	}

	payload := map[string]string{
		"feedback_type": feedback.FeedbackType,
		"checklist":     feedback.Checklist,
		"message":       feedback.Message,
		"submitted_by":  email,
	}

	jsonBody, err := json.Marshal(payload)
	if err != nil {
		log.Printf("checklists feedback marshal error: %v", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Interner Fehler."})
		return
	}

	endpoint := fmt.Sprintf("%s/rest/v1/checklist_feedback", authClient.URL)
	req2, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonBody))
	if err != nil {
		log.Printf("checklists feedback request error: %v", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Interner Fehler."})
		return
	}
	req2.Header.Set("Content-Type", "application/json")
	req2.Header.Set("apikey", authClient.AnonKey)
	if accessToken != "" {
		req2.Header.Set("Authorization", "Bearer "+accessToken)
	} else {
		req2.Header.Set("Authorization", "Bearer "+authClient.AnonKey)
	}
	req2.Header.Set("Prefer", "return=minimal")

	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(req2)
	if err != nil {
		log.Printf("checklists feedback supabase error: %v", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Fehler beim Speichern."})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 300 {
		body, _ := io.ReadAll(resp.Body)
		log.Printf("checklists feedback supabase status %d: %s", resp.StatusCode, string(body))
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Fehler beim Speichern."})
		return
	}

	writeJSON(w, http.StatusCreated, map[string]string{"ok": "true"})
}
