package handlers

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/bridge/ai-customer-service/internal/domain/audit"
	"github.com/bridge/ai-customer-service/internal/domain/error/cserrors"
	"github.com/bridge/ai-customer-service/internal/domain/ticket"
	"github.com/bridge/ai-customer-service/internal/http/middleware"
)

type TicketService interface {
	ListOpen(ctx context.Context, limit int) ([]ticket.Ticket, error)
	GetByID(ctx context.Context, id string) (*ticket.Ticket, error)
	Assign(ctx context.Context, ticketID, agentID, actorID, sourceIP string, now time.Time) error
	Resolve(ctx context.Context, ticketID, resolution, actorID, sourceIP string, now time.Time) error
	Close(ctx context.Context, ticketID, resolution, actorID, sourceIP string, now time.Time) error
}

type TicketHandler struct {
	service TicketService
	audit   AuditRecorder
	now     func() time.Time
}

func NewTicketHandler(service TicketService, auditRecorder AuditRecorder) *TicketHandler {
	return &TicketHandler{service: service, audit: auditRecorder, now: time.Now}
}

func (h *TicketHandler) List(w http.ResponseWriter, r *http.Request) {
	items, err := h.service.ListOpen(r.Context(), 50)
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"error": map[string]any{"code": cserrors.CS_SYS_5002, "message": cserrors.ErrorMsg(cserrors.CS_SYS_5002)}})
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"items": items})
}

// P1-3: GET /api/v1/customer-service/tickets/{id} — ticket detail (Phase 1 minimum implementation)
func (h *TicketHandler) Get(w http.ResponseWriter, r *http.Request) {
	ticketID := pathParam(r.URL.Path, "/api/v1/customer-service/tickets/", "")
	if ticketID == "" {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": map[string]any{"code": cserrors.CS_REQ_4005, "message": cserrors.ErrorMsg(cserrors.CS_REQ_4005)}})
		return
	}
	tkt, err := h.service.GetByID(r.Context(), ticketID)
	if err != nil || tkt == nil {
		writeJSON(w, http.StatusNotFound, map[string]any{"error": map[string]any{"code": cserrors.CS_TICKET_4001, "message": cserrors.ErrorMsg(cserrors.CS_TICKET_4001)}})
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"ticket": tkt})
}

func (h *TicketHandler) Assign(w http.ResponseWriter, r *http.Request) {
	ticketID := pathParam(r.URL.Path, "/api/v1/customer-service/tickets/", "/assign")
	agentID := strings.TrimSpace(r.URL.Query().Get("agent_id"))
	if ticketID == "" || agentID == "" {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": map[string]any{"code": cserrors.CS_REQ_4005, "message": cserrors.ErrorMsg(cserrors.CS_REQ_4005)}})
		return
	}
	actor, ok := middleware.ActorFromContext(r.Context())
	if !ok {
		writeJSON(w, http.StatusForbidden, map[string]any{"error": map[string]any{"code": cserrors.CS_AUTH_4001, "message": cserrors.ErrorMsg(cserrors.CS_AUTH_4001)}})
		return
	}
	actorID := actor.ID
	sourceIP := clientIP(r.RemoteAddr)
	if err := h.service.Assign(r.Context(), ticketID, agentID, actorID, sourceIP, h.now()); err != nil {
		// P0-2 fix: route error based on error code prefix from service layer
		errStr := err.Error()
		if strings.HasPrefix(errStr, "CS_TICKET_4001") {
			writeJSON(w, http.StatusNotFound, map[string]any{"error": map[string]any{"code": cserrors.CS_TICKET_4001, "message": cserrors.ErrorMsg(cserrors.CS_TICKET_4001)}})
			return
		}
		writeJSON(w, http.StatusConflict, map[string]any{"error": map[string]any{"code": cserrors.CS_TKT_4002, "message": cserrors.ErrorMsg(cserrors.CS_TKT_4002)}})
		return
	}
	h.auditTicketChange(r.Context(), ticketID, "assign", actorID, map[string]any{"assigned_to": agentID, "status": ticket.StatusAssigned}, r.RemoteAddr)
	writeJSON(w, http.StatusOK, map[string]any{"assigned": true})
}

func (h *TicketHandler) Resolve(w http.ResponseWriter, r *http.Request) {
	ticketID := pathParam(r.URL.Path, "/api/v1/customer-service/tickets/", "/resolve")
	resolution := strings.TrimSpace(r.URL.Query().Get("resolution"))
	if ticketID == "" || resolution == "" {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": map[string]any{"code": cserrors.CS_REQ_4006, "message": cserrors.ErrorMsg(cserrors.CS_REQ_4006)}})
		return
	}
	actor, ok := middleware.ActorFromContext(r.Context())
	if !ok {
		writeJSON(w, http.StatusForbidden, map[string]any{"error": map[string]any{"code": cserrors.CS_AUTH_4001, "message": cserrors.ErrorMsg(cserrors.CS_AUTH_4001)}})
		return
	}
	actorID := actor.ID
	sourceIP := clientIP(r.RemoteAddr)
	if err := h.service.Resolve(r.Context(), ticketID, resolution, actorID, sourceIP, h.now()); err != nil {
		// P0-2 fix: route error based on error code prefix from service layer
		errStr := err.Error()
		if strings.HasPrefix(errStr, "CS_TICKET_4001") {
			writeJSON(w, http.StatusNotFound, map[string]any{"error": map[string]any{"code": cserrors.CS_TICKET_4001, "message": cserrors.ErrorMsg(cserrors.CS_TICKET_4001)}})
			return
		}
		writeJSON(w, http.StatusConflict, map[string]any{"error": map[string]any{"code": cserrors.CS_TICKET_4092, "message": cserrors.ErrorMsg(cserrors.CS_TICKET_4092)}})
		return
	}
	h.auditTicketChange(r.Context(), ticketID, "resolve", actorID, map[string]any{"resolution": resolution, "status": ticket.StatusResolved}, r.RemoteAddr)
	writeJSON(w, http.StatusOK, map[string]any{"resolved": true})
}

func (h *TicketHandler) Close(w http.ResponseWriter, r *http.Request) {
	ticketID := pathParam(r.URL.Path, "/api/v1/customer-service/tickets/", "/close")
	resolution := strings.TrimSpace(r.URL.Query().Get("resolution"))
	if ticketID == "" || resolution == "" {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": map[string]any{"code": cserrors.CS_REQ_4007, "message": cserrors.ErrorMsg(cserrors.CS_REQ_4007)}})
		return
	}
	actor, ok := middleware.ActorFromContext(r.Context())
	if !ok {
		writeJSON(w, http.StatusForbidden, map[string]any{"error": map[string]any{"code": cserrors.CS_AUTH_4001, "message": cserrors.ErrorMsg(cserrors.CS_AUTH_4001)}})
		return
	}
	actorID := actor.ID
	sourceIP := clientIP(r.RemoteAddr)
	if err := h.service.Close(r.Context(), ticketID, resolution, actorID, sourceIP, h.now()); err != nil {
		// P0-2 fix: route error based on error code prefix from service layer
		errStr := err.Error()
		if strings.HasPrefix(errStr, "CS_TICKET_4001") {
			writeJSON(w, http.StatusNotFound, map[string]any{"error": map[string]any{"code": cserrors.CS_TICKET_4001, "message": cserrors.ErrorMsg(cserrors.CS_TICKET_4001)}})
			return
		}
		writeJSON(w, http.StatusConflict, map[string]any{"error": map[string]any{"code": cserrors.CS_TICKET_4093, "message": cserrors.ErrorMsg(cserrors.CS_TICKET_4093)}})
		return
	}
	h.auditTicketChange(r.Context(), ticketID, "close", actorID, map[string]any{"resolution": resolution, "status": ticket.StatusClosed}, r.RemoteAddr)
	writeJSON(w, http.StatusOK, map[string]any{"closed": true})
}

func (h *TicketHandler) auditTicketChange(ctx context.Context, ticketID, action, actorID string, after map[string]any, remoteAddr string) {
	if h == nil || h.audit == nil {
		return
	}
	now := h.now()
	// P0 quality standard: audit write failure only logs, does not return error
	_ = h.audit.Add(ctx, audit.Event{ID: newAuditID("audit", now), Type: "ticket_state_changed", Action: action, TicketID: ticketID, ActorID: actorID, SourceIP: clientIP(remoteAddr), AfterState: after, CreatedAt: now})
}

func pathParam(path, prefix, suffix string) string {
	trimmed := strings.TrimPrefix(path, prefix)
	trimmed = strings.TrimSuffix(trimmed, suffix)
	trimmed = strings.Trim(trimmed, "/")
	return trimmed
}
