package handlers

import (
	"errors"
	"net/http"
	"strconv"
	"strings"

	"github.com/acp/interceptor/internal/approval"
	"github.com/acp/interceptor/internal/audit"
	"github.com/acp/interceptor/internal/services"
	"github.com/gin-gonic/gin"
)

// ApprovalHandler serves human-in-the-loop approval APIs for the dashboard and clients.
type ApprovalHandler struct {
	store  approval.Repository
	events audit.EventStore
	audit  *services.AuditService
}

// NewApprovalHandler constructs an approval handler.
func NewApprovalHandler(store approval.Repository, events audit.EventStore, auditSvc *services.AuditService) *ApprovalHandler {
	return &ApprovalHandler{store: store, events: events, audit: auditSvc}
}

type decideBody struct {
	DecidedBy string `json:"decided_by"`
}

// ListApprovals returns approval tickets, optionally filtered by status.
func (h *ApprovalHandler) ListApprovals(c *gin.Context) {
	limit := 50
	if v := c.Query("limit"); v != "" {
		if n, err := strconv.Atoi(v); err == nil && n > 0 {
			limit = n
		}
	}
	status := approval.Status(strings.TrimSpace(c.Query("status")))
	if status != "" && status != approval.StatusPending && status != approval.StatusApproved && status != approval.StatusDenied {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid status"})
		return
	}
	if h.events != nil && (status == "" || status == approval.StatusPending) {
		approval.SyncPendingFromAudit(h.store, h.events.List(200))
	}
	c.JSON(http.StatusOK, gin.H{
		"approvals": h.store.List(status, limit),
	})
}

// GetApproval returns one ticket by trace_id.
func (h *ApprovalHandler) GetApproval(c *gin.Context) {
	traceID := strings.TrimSpace(c.Param("trace_id"))
	rec, ok := h.store.Get(traceID)
	if !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": "approval not found"})
		return
	}
	c.JSON(http.StatusOK, rec)
}

// Approve marks a pending ticket approved.
func (h *ApprovalHandler) Approve(c *gin.Context) {
	h.decide(c, true)
}

// Deny marks a pending ticket denied.
func (h *ApprovalHandler) Deny(c *gin.Context) {
	h.decide(c, false)
}

func (h *ApprovalHandler) decide(c *gin.Context, approve bool) {
	traceID := strings.TrimSpace(c.Param("trace_id"))
	var body decideBody
	_ = c.ShouldBindJSON(&body)
	decidedBy := strings.TrimSpace(body.DecidedBy)
	if decidedBy == "" {
		decidedBy = "dashboard"
	}

	var (
		rec *approval.Record
		err error
	)
	if approve {
		rec, err = h.store.Approve(traceID, decidedBy)
	} else {
		rec, err = h.store.Deny(traceID, decidedBy)
	}
	if err != nil {
		if errors.Is(err, approval.ErrNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "approval not found"})
			return
		}
		if errors.Is(err, approval.ErrNotPending) {
			c.JSON(http.StatusConflict, gin.H{"error": "approval is not pending"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "approval update failed"})
		return
	}

	decision := "denied"
	if approve {
		decision = "approved"
	}
	h.audit.LogToolCallEvaluated(audit.Event{
		Type:     "approval_decided",
		TraceID:  traceID,
		AgentID:  rec.AgentID,
		Tool:     rec.Tool,
		Action:   rec.Action,
		Decision: decision,
		Reason:   "human_" + decision,
		Policy:   rec.Policy,
		Amount:   rec.Amount,
		RiskScore: rec.RiskScore,
	})

	c.JSON(http.StatusOK, rec)
}
