package handlers

// paliadin_suggest.go — agent-suggested write path (t-paliad-161 Slice D).
//
// HTTP endpoints invoked by Paliadin (via tools the SKILL.md describes)
// to draft an entity on the user's behalf. The drafted entity goes
// through the existing approval pipeline as approval_status='pending'
// with requester_kind='agent' + agent_turn_id, surfaced to the user via
// the eye-pill 👀 + sparkle ✨.
//
// Routes (gated to PaliadinOwnerEmail — the same gate /paliadin uses):
//   POST /api/paliadin/suggest/deadline
//   POST /api/paliadin/suggest/appointment
//
// Body shape (deadline):
//   { "turn_id":    "<uuid>", "project_id":"<uuid>", "title":"...",
//     "due_date":   "YYYY-MM-DD", "notes":"..." (optional),
//     "rule_code":  "..." (optional) }
//
// Body shape (appointment):
//   { "turn_id":   "<uuid>", "project_id":"<uuid>", "title":"...",
//     "start_at":  "RFC3339", "end_at":"RFC3339" (optional),
//     "location":  "..." (optional), "appointment_type":"..." (optional) }
//
// On success returns 201 + the created entity. On policy / permission
// failure, returns the same error codes the regular create paths use.

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/google/uuid"

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

// suggestDeadlineRequest is the body POST /api/paliadin/suggest/deadline
// accepts. turn_id MUST refer to a row Paliadin actually wrote — the
// approval_requests.agent_turn_id FK rejects forged uuids.
type suggestDeadlineRequest struct {
	TurnID    string  `json:"turn_id"`
	ProjectID string  `json:"project_id"`
	Title     string  `json:"title"`
	DueDate   string  `json:"due_date"` // YYYY-MM-DD
	Notes     *string `json:"notes,omitempty"`
	RuleCode  *string `json:"rule_code,omitempty"`
}

type suggestAppointmentRequest struct {
	TurnID          string  `json:"turn_id"`
	ProjectID       string  `json:"project_id"`
	Title           string  `json:"title"`
	StartAt         string  `json:"start_at"` // RFC3339
	EndAt           *string `json:"end_at,omitempty"`
	Location        *string `json:"location,omitempty"`
	AppointmentType *string `json:"appointment_type,omitempty"`
}

// handlePaliadinSuggestDeadline drafts a deadline through the approval
// gate. Returns the created deadline (with approval_status='pending').
func handlePaliadinSuggestDeadline(w http.ResponseWriter, r *http.Request) {
	if !requirePaliadinOwner(w, r) {
		return
	}
	if dbSvc == nil || dbSvc.deadline == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "deadline service unavailable"})
		return
	}
	uid, _ := requireUser(w, r)

	var req suggestDeadlineRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	turnID, err := uuid.Parse(strings.TrimSpace(req.TurnID))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid turn_id"})
		return
	}
	projectID, err := uuid.Parse(strings.TrimSpace(req.ProjectID))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid project_id"})
		return
	}
	if strings.TrimSpace(req.Title) == "" || strings.TrimSpace(req.DueDate) == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "title and due_date required"})
		return
	}

	input := services.CreateDeadlineInput{
		Title:       req.Title,
		DueDate:     req.DueDate,
		Notes:       req.Notes,
		RuleCode:    req.RuleCode,
		Source:      "paliadin",
		AgentTurnID: &turnID,
	}
	d, err := dbSvc.deadline.Create(r.Context(), uid, projectID, input)
	if err != nil {
		mapSuggestError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, d)
}

// handlePaliadinSuggestAppointment drafts an appointment through the
// approval gate. Returns the created appointment (with
// approval_status='pending').
func handlePaliadinSuggestAppointment(w http.ResponseWriter, r *http.Request) {
	if !requirePaliadinOwner(w, r) {
		return
	}
	if dbSvc == nil || dbSvc.appointment == nil {
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "appointment service unavailable"})
		return
	}
	uid, _ := requireUser(w, r)

	var req suggestAppointmentRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	turnID, err := uuid.Parse(strings.TrimSpace(req.TurnID))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid turn_id"})
		return
	}
	projectID, err := uuid.Parse(strings.TrimSpace(req.ProjectID))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid project_id"})
		return
	}
	if strings.TrimSpace(req.Title) == "" || strings.TrimSpace(req.StartAt) == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "title and start_at required"})
		return
	}
	startAt, err := time.Parse(time.RFC3339, req.StartAt)
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid start_at: " + err.Error()})
		return
	}
	var endAt *time.Time
	if req.EndAt != nil && strings.TrimSpace(*req.EndAt) != "" {
		t, err := time.Parse(time.RFC3339, *req.EndAt)
		if err != nil {
			writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid end_at: " + err.Error()})
			return
		}
		endAt = &t
	}

	input := services.CreateAppointmentInput{
		ProjectID:       &projectID,
		Title:           req.Title,
		StartAt:         startAt,
		EndAt:           endAt,
		Location:        req.Location,
		AppointmentType: req.AppointmentType,
		AgentTurnID:     &turnID,
	}
	a, err := dbSvc.appointment.Create(r.Context(), uid, input)
	if err != nil {
		mapSuggestError(w, err)
		return
	}
	writeJSON(w, http.StatusCreated, a)
}

// mapSuggestError translates ApprovalService / DeadlineService errors
// into HTTP responses. Mirrors the existing approval-error mapping in
// handlers/approvals.go (mapApprovalError) but with a payload shape the
// Paliadin shim can read back into a useful chip in the response.
func mapSuggestError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, services.ErrNotVisible):
		writeJSON(w, http.StatusForbidden, map[string]string{"error": "no visibility on project"})
	case errors.Is(err, services.ErrInvalidInput):
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
	case errors.Is(err, services.ErrNoQualifiedApprover):
		writeJSON(w, http.StatusConflict, map[string]string{
			"error": "no qualified approver on this project — invite an associate or higher before suggesting",
		})
	default:
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("suggest failed: %v", err)})
	}
}
