package settlement

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

	"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 ingestProviderReportRequest struct {
	Provider        string                     `json:"provider"`
	ReportReference string                     `json:"report_reference"`
	ReportType      string                     `json:"report_type"`
	Items           []ProviderReportItemParams `json:"items"`
}

type processInboundPaymentRequest struct {
	Provider          string          `json:"provider"`
	ProviderReference string          `json:"provider_reference"`
	DebtorName        string          `json:"debtor_name"`
	DebtorIBAN        string          `json:"debtor_iban"`
	CreditorIBAN      string          `json:"creditor_iban"`
	AmountCents       int64           `json:"amount_cents"`
	Currency          string          `json:"currency"`
	RawPayload        json.RawMessage `json:"raw_payload"`
}

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

func (h *Handler) IngestProviderReport(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	var req ingestProviderReportRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	raw, err := json.Marshal(req)
	if err != nil {
		respond.Error(w, err)
		return
	}

	report, err := h.repo.IngestProviderReport(r.Context(), ProviderReportParams{
		Provider:        req.Provider,
		ReportReference: req.ReportReference,
		ReportType:      req.ReportType,
		RawPayload:      raw,
		Items:           req.Items,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}

	h.recordAudit(r, claims.Subject, "admin.sepa_provider_report.ingested", "sepa_provider_report", report.ID, map[string]any{
		"provider":         report.Provider,
		"report_reference": report.ReportReference,
		"item_count":       report.ItemCount,
		"processed_count":  report.ProcessedCount,
		"error_count":      report.ErrorCount,
	})
	respond.Created(w, report)
}

func (h *Handler) ProviderReports(w http.ResponseWriter, r *http.Request) {
	reports, err := h.repo.ListProviderReports(r.Context(), queryLimit(r))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"sepa_provider_reports": reports})
}

func (h *Handler) ProcessInboundPayment(w http.ResponseWriter, r *http.Request) {
	claims, ok := middleware.CurrentClaims(r)
	if !ok {
		respond.Error(w, domain.ErrUnauthorized)
		return
	}

	var req processInboundPaymentRequest
	if err := respond.DecodeJSON(r, &req); err != nil {
		respond.Error(w, err)
		return
	}
	raw := req.RawPayload
	if len(raw) == 0 {
		encoded, err := json.Marshal(req)
		if err != nil {
			respond.Error(w, err)
			return
		}
		raw = encoded
	}

	inbound, err := h.repo.ProcessInboundPayment(r.Context(), InboundPaymentParams{
		Provider:          req.Provider,
		ProviderReference: req.ProviderReference,
		DebtorName:        req.DebtorName,
		DebtorIBAN:        req.DebtorIBAN,
		CreditorIBAN:      req.CreditorIBAN,
		AmountCents:       req.AmountCents,
		Currency:          req.Currency,
		RawPayload:        raw,
	})
	if err != nil {
		respond.Error(w, err)
		return
	}

	h.recordAudit(r, claims.Subject, "admin.inbound_payment.processed", "inbound_payment", inbound.ID, map[string]any{
		"provider":           inbound.Provider,
		"provider_reference": inbound.ProviderReference,
		"account_id":         inbound.AccountID,
		"amount_cents":       inbound.AmountCents,
		"currency":           inbound.Currency,
		"status":             inbound.Status,
	})
	respond.Created(w, inbound)
}

func (h *Handler) InboundPayments(w http.ResponseWriter, r *http.Request) {
	payments, err := h.repo.ListInboundPayments(r.Context(), queryLimit(r))
	if err != nil {
		respond.Error(w, err)
		return
	}
	respond.JSON(w, http.StatusOK, map[string]any{"inbound_payments": payments})
}

func (h *Handler) recordAudit(r *http.Request, actorID, eventType, targetType, targetID string, metadata any) {
	if h.audit == nil {
		return
	}
	if err := h.audit.Record(r.Context(), audit.Event{
		ActorUserID: &actorID,
		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)
	}
}

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