package golive

import (
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/niels/banking-app/backend/internal/audit"
	"github.com/niels/banking-app/backend/internal/domain"
	"github.com/niels/banking-app/backend/internal/httpapi/middleware"
	"github.com/niels/banking-app/backend/internal/platform/httputil"
	"github.com/niels/banking-app/backend/internal/respond"
)

type Handler struct {
	repo  *Repository
	audit *audit.Repository
	log   *slog.Logger
}

type gateRequest struct {
	GateNumber        int             `json:"gate_number"`
	Phase             string          `json:"phase"`
	GateKey           string          `json:"gate_key"`
	Title             string          `json:"title"`
	Status            string          `json:"status"`
	Priority          string          `json:"priority"`
	OwnerTeam         string          `json:"owner_team"`
	DecisionSummary   string          `json:"decision_summary"`
	EvidenceReference string          `json:"evidence_reference"`
	RunbookReference  string          `json:"runbook_reference"`
	ApproverName      string          `json:"approver_name"`
	DueAt             string          `json:"due_at"`
	Metadata          json.RawMessage `json:"metadata"`
}

func NewHandler(repo *Repository, auditRepo *audit.Repository, log *slog.Logger) *Handler {
	return &Handler{repo: repo, audit: auditRepo, log: log}
}

func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) {
	dashboard, err := h.repo.Dashboard(r.Context(), queryLimit(r))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, dashboard)
}

func (h *Handler) Gates(w http.ResponseWriter, r *http.Request) {
	h.listGates(w, r, strings.TrimSpace(r.URL.Query().Get("phase")), "go_live_gates")
}

func (h *Handler) UpsertGate(w http.ResponseWriter, r *http.Request) {
	h.upsertGate(w, r, "")
}

func (h *Handler) StagingGates(w http.ResponseWriter, r *http.Request) {
	h.listGates(w, r, PhaseStaging, "staging_gates")
}

func (h *Handler) UpsertStagingGate(w http.ResponseWriter, r *http.Request) {
	h.upsertGate(w, r, PhaseStaging)
}

func (h *Handler) BetaGates(w http.ResponseWriter, r *http.Request) {
	h.listGates(w, r, PhaseClosedBeta, "beta_gates")
}

func (h *Handler) UpsertBetaGate(w http.ResponseWriter, r *http.Request) {
	h.upsertGate(w, r, PhaseClosedBeta)
}

func (h *Handler) ProductionGates(w http.ResponseWriter, r *http.Request) {
	h.listGates(w, r, PhaseProduction, "production_gates")
}

func (h *Handler) UpsertProductionGate(w http.ResponseWriter, r *http.Request) {
	h.upsertGate(w, r, PhaseProduction)
}

func (h *Handler) listGates(w http.ResponseWriter, r *http.Request, phase, responseKey string) {
	gates, err := h.repo.ListGates(
		r.Context(),
		phase,
		strings.TrimSpace(r.URL.Query().Get("status")),
		queryLimit(r),
	)
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{responseKey: gates})
}

func (h *Handler) upsertGate(w http.ResponseWriter, r *http.Request, forcedPhase string) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}
	var req gateRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	if forcedPhase != "" {
		req.Phase = forcedPhase
	}
	dueAt, err := parseOptionalTime(req.DueAt, "due_at")
	if err != nil {
		respond.Error(w, err)
		return
	}
	gate, err := h.repo.UpsertGate(r.Context(), GateParams{
		GateNumber:        req.GateNumber,
		Phase:             req.Phase,
		GateKey:           req.GateKey,
		Title:             req.Title,
		Status:            req.Status,
		Priority:          req.Priority,
		OwnerTeam:         req.OwnerTeam,
		DecisionSummary:   req.DecisionSummary,
		EvidenceReference: req.EvidenceReference,
		RunbookReference:  req.RunbookReference,
		ApproverName:      req.ApproverName,
		DueAt:             dueAt,
		Metadata:          req.Metadata,
		AdminUserID:       claims.Subject,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}
	h.recordAudit(r, claims.Subject, "admin.go_live_gate.upserted", "go_live_gate", gate.ID, map[string]any{
		"gate_number": gate.GateNumber,
		"gate_key":    gate.GateKey,
		"phase":       gate.Phase,
		"status":      gate.Status,
	})
	respond.JSON(w, http.StatusOK, gate)
}

func parseOptionalTime(raw, field string) (*time.Time, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return nil, nil
	}
	parsed, err := time.Parse(time.RFC3339, raw)
	if err != nil {
		return nil, fmt.Errorf("%w: %s must be RFC3339", domain.ErrValidation, field)
	}
	utc := parsed.UTC()
	return &utc, nil
}

func queryLimit(r *http.Request) int {
	limit := 50
	if raw := r.URL.Query().Get("limit"); raw != "" {
		if parsed, err := strconv.Atoi(raw); err == nil {
			limit = parsed
		}
	}
	return limit
}

func (h *Handler) recordAudit(r *http.Request, adminUserID, eventType, targetType, targetID string, metadata any) {
	if h.audit == nil {
		return
	}
	if err := h.audit.Record(r.Context(), audit.Event{
		ActorUserID: &adminUserID,
		EventType:   eventType,
		TargetType:  targetType,
		TargetID:    targetID,
		Metadata:    metadata,
		RemoteIP:    httputil.RemoteIP(r),
		UserAgent:   r.UserAgent(),
	}); err != nil && h.log != nil {
		h.log.Error("audit record failed", "error", err)
	}
}
