package handlers

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

	"github.com/google/uuid"

	"mgit.msbls.de/m/paliad/internal/services"
	lp "mgit.msbls.de/m/paliad/pkg/litigationplanner"
)

// Slice D (m/paliad#124 §5, mig 145) — REST endpoints for paliad.scenarios.
//
// Routes (registered in handlers.go):
//
//	GET    /api/scenarios?project=<id>     — list project's scenarios
//	GET    /api/scenarios?abstract=true    — list caller's abstract scenarios
//	GET    /api/scenarios/{id}             — fetch one
//	POST   /api/scenarios                  — create
//	PATCH  /api/scenarios/{id}             — partial update
//	PUT    /api/projects/{id}/active-scenario  — set/clear active scenario
//	DELETE /api/scenarios/{id}             — remove
//
// All endpoints require auth; visibility is enforced by
// ScenarioService.requireProjectVisible / requireVisible.

func requireScenarioService(w http.ResponseWriter) bool {
	if dbSvc == nil || dbSvc.scenario == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{
			"error": "Szenarien sind vorübergehend nicht verfügbar (keine Datenbank).",
		})
		return false
	}
	return true
}

// scenarioErrorToStatus maps service errors to HTTP statuses. Mirrors
// the patterns in projects.go and event_choices.go.
func scenarioErrorToStatus(err error) (int, string) {
	switch {
	case errors.Is(err, lp.ErrUnknownScenario), errors.Is(err, services.ErrScenarioNotVisible):
		return http.StatusNotFound, "Szenario nicht gefunden"
	case errors.Is(err, services.ErrInvalidInput), errors.Is(err, lp.ErrInvalidScenario), errors.Is(err, lp.ErrScenarioNoPrimary):
		return http.StatusBadRequest, err.Error()
	}
	return http.StatusInternalServerError, err.Error()
}

// handleScenariosList — GET /api/scenarios?project=<uuid> OR ?abstract=true.
func handleScenariosList(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}

	abstract := r.URL.Query().Get("abstract") == "true"
	projectStr := r.URL.Query().Get("project")
	switch {
	case abstract:
		out, err := dbSvc.scenario.ListAbstractForUser(r.Context(), uid)
		if err != nil {
			status, msg := scenarioErrorToStatus(err)
			writeJSON(w, status, map[string]string{"error": msg})
			return
		}
		writeJSON(w, http.StatusOK, out)
	case projectStr != "":
		pid, err := uuid.Parse(projectStr)
		if err != nil {
			writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige project ID"})
			return
		}
		out, err := dbSvc.scenario.ListForProject(r.Context(), uid, pid)
		if err != nil {
			status, msg := scenarioErrorToStatus(err)
			writeJSON(w, status, map[string]string{"error": msg})
			return
		}
		writeJSON(w, http.StatusOK, out)
	default:
		writeJSON(w, http.StatusBadRequest, map[string]string{
			"error": "?project=<uuid> oder ?abstract=true erforderlich",
		})
	}
}

// handleScenarioGet — GET /api/scenarios/{id}.
func handleScenarioGet(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige ID"})
		return
	}
	out, err := dbSvc.scenario.Get(r.Context(), uid, id)
	if err != nil {
		status, msg := scenarioErrorToStatus(err)
		writeJSON(w, status, map[string]string{"error": msg})
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleScenarioCreate — POST /api/scenarios.
func handleScenarioCreate(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	var input services.CreateScenarioInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenario.Create(r.Context(), uid, input)
	if err != nil {
		status, msg := scenarioErrorToStatus(err)
		writeJSON(w, status, map[string]string{"error": msg})
		return
	}
	writeJSON(w, http.StatusCreated, out)
}

// handleScenarioPatch — PATCH /api/scenarios/{id}.
func handleScenarioPatch(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige ID"})
		return
	}
	var input services.PatchScenarioInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	out, err := dbSvc.scenario.Patch(r.Context(), uid, id, input)
	if err != nil {
		status, msg := scenarioErrorToStatus(err)
		writeJSON(w, status, map[string]string{"error": msg})
		return
	}
	writeJSON(w, http.StatusOK, out)
}

// handleScenarioDelete — DELETE /api/scenarios/{id}.
func handleScenarioDelete(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	id, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige ID"})
		return
	}
	if err := dbSvc.scenario.Delete(r.Context(), uid, id); err != nil {
		status, msg := scenarioErrorToStatus(err)
		writeJSON(w, status, map[string]string{"error": msg})
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

// handleSetActiveScenario — PUT /api/projects/{id}/active-scenario.
// Body: {"scenario_id": "<uuid>"} or {"scenario_id": null} to clear.
func handleSetActiveScenario(w http.ResponseWriter, r *http.Request) {
	if !requireScenarioService(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	pid, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige project ID"})
		return
	}
	var body struct {
		ScenarioID *uuid.UUID `json:"scenario_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "ungültige Anfrage"})
		return
	}
	if err := dbSvc.scenario.SetActive(r.Context(), uid, pid, body.ScenarioID); err != nil {
		status, msg := scenarioErrorToStatus(err)
		writeJSON(w, status, map[string]string{"error": msg})
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
