package handlers

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

	"github.com/google/uuid"

	"mgit.msbls.de/m/paliad/internal/services"
)

// GET /api/deadlines?status=overdue|this_week|upcoming|completed|pending|all&project_id=UUID&event_type=<uuid>,<uuid>,none
func handleListDeadlines(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	filter := services.ListFilter{
		Status: services.DeadlineStatusFilter(r.URL.Query().Get("status")),
	}
	// Accept both project_id (new) and project_id (legacy alias).
	raw := r.URL.Query().Get("project_id")
	if raw == "" {
		raw = r.URL.Query().Get("project_id")
	}
	if raw != "" {
		projectID, err := uuid.Parse(raw)
		if err != nil {
			writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid project_id"})
			return
		}
		filter.ProjectID = &projectID
	}
	ids, untyped, err := parseEventTypeFilter(r.URL.Query().Get("event_type"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	filter.EventTypeIDs = ids
	filter.IncludeUntyped = untyped

	rows, err := dbSvc.deadline.ListVisibleForUser(r.Context(), uid, filter)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, rows)
}

// parseEventTypeFilter parses the comma-separated `event_type` query
// parameter used by both /api/deadlines and /api/agenda. The literal
// keyword "none" is the toggle for "deadlines without any Event Type
// attached" (filter.IncludeUntyped). All other tokens must be valid
// UUIDs of visible event_types — the service layer validates visibility.
//
// Returns (ids, includeUntyped, err). Empty/blank input returns
// (nil, false, nil) — interpret as "no filter".
func parseEventTypeFilter(raw string) ([]uuid.UUID, bool, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return nil, false, nil
	}
	ids := []uuid.UUID{}
	includeUntyped := false
	for tok := range strings.SplitSeq(raw, ",") {
		t := strings.TrimSpace(tok)
		if t == "" {
			continue
		}
		if t == "none" {
			includeUntyped = true
			continue
		}
		id, err := uuid.Parse(t)
		if err != nil {
			return nil, false, &agendaErr{msg: "invalid event_type id " + t}
		}
		ids = append(ids, id)
	}
	return ids, includeUntyped, nil
}

// GET /api/deadlines/summary?project_id=UUID
func handleDeadlinesSummary(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	var projectIDPtr *uuid.UUID
	raw := r.URL.Query().Get("project_id")
	if raw == "" {
		raw = r.URL.Query().Get("project_id")
	}
	if raw != "" {
		projectID, err := uuid.Parse(raw)
		if err != nil {
			writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid project_id"})
			return
		}
		projectIDPtr = &projectID
	}
	c, err := dbSvc.deadline.SummaryCounts(r.Context(), uid, projectIDPtr)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, c)
}

// GET /api/projects/{id}/deadlines
func handleListDeadlinesForProject(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	projectID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
		return
	}
	directOnly := parseDirectOnly(r.URL.Query().Get("direct_only"))
	rows, err := dbSvc.deadline.ListForProject(r.Context(), uid, projectID, directOnly)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, rows)
}

// POST /api/projects/{id}/deadlines
func handleCreateDeadline(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	projectID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
		return
	}
	var input services.CreateDeadlineInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	f, err := dbSvc.deadline.Create(r.Context(), uid, projectID, input)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, f)
}

// POST /api/projects/{id}/deadlines/bulk — Fristenrechner "save to Project".
func handleBulkCreateDeadlines(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	projectID, err := uuid.Parse(r.PathValue("id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid id"})
		return
	}
	var body struct {
		Deadlines []services.CreateDeadlineInput `json:"deadlines"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	rows, err := dbSvc.deadline.CreateBulk(r.Context(), uid, projectID, body.Deadlines)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, rows)
}

// GET /api/deadlines/{id}
func handleGetDeadline(w http.ResponseWriter, r *http.Request) {
	if !requireDB(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": "invalid id"})
		return
	}
	f, err := dbSvc.deadline.GetByID(r.Context(), uid, id)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, f)
}

// PATCH /api/deadlines/{id}
func handleUpdateDeadline(w http.ResponseWriter, r *http.Request) {
	if !requireDB(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": "invalid id"})
		return
	}
	var input services.UpdateDeadlineInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	f, err := dbSvc.deadline.Update(r.Context(), uid, id, input)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, f)
}

// PATCH /api/deadlines/{id}/complete — convenience endpoint for the list-row checkbox.
func handleCompleteDeadline(w http.ResponseWriter, r *http.Request) {
	if !requireDB(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": "invalid id"})
		return
	}
	f, err := dbSvc.deadline.Complete(r.Context(), uid, id)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, f)
}

// PATCH /api/deadlines/{id}/reopen — admin/project-lead-only undo of complete.
func handleReopenDeadline(w http.ResponseWriter, r *http.Request) {
	if !requireDB(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": "invalid id"})
		return
	}
	f, err := dbSvc.deadline.Reopen(r.Context(), uid, id)
	if err != nil {
		writeServiceError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, f)
}

// DELETE /api/deadlines/{id}
func handleDeleteDeadline(w http.ResponseWriter, r *http.Request) {
	if !requireDB(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": "invalid id"})
		return
	}
	if err := dbSvc.deadline.Delete(r.Context(), uid, id); err != nil {
		writeServiceError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}
