import type { RampSettlementEvent } from "@sdp/payments/ramps";
import type { Context } from "hono";
import type { PaymentTransferStatus } from "@/db/repositories";
import { createSystemPaymentsRepository, isRampTransferType } from "@/db/repositories";
import type { Env } from "@/types/env";

type AppContext = Context<{ Bindings: Env }>;

const RAMP_SETTLEMENT_STATUS = {
  awaiting_payment: "awaiting_payment",
  settling: "settling",
  settled: "completed",
  failed: "failed",
  expired: "expired",
} as const satisfies Record<Exclude<RampSettlementEvent["kind"], "ignore">, PaymentTransferStatus>;

const TERMINAL_RAMP_TRANSFER_STATUSES = [
  "completed",
  "failed",
  "expired",
  "canceled",
] as const satisfies readonly PaymentTransferStatus[];

const ALLOWED_RAMP_SETTLEMENT_SOURCE_STATUSES = {
  awaiting_payment: ["pending"],
  settling: ["pending", "awaiting_payment"],
  settled: ["pending", "awaiting_payment", "settling"],
  failed: ["pending", "awaiting_payment", "settling"],
  expired: ["pending", "awaiting_payment", "settling"],
} as const satisfies Record<
  Exclude<RampSettlementEvent["kind"], "ignore">,
  readonly PaymentTransferStatus[]
>;

function isTerminalRampTransferStatus(status: PaymentTransferStatus): boolean {
  return (TERMINAL_RAMP_TRANSFER_STATUSES as readonly PaymentTransferStatus[]).includes(status);
}

export async function applyRampSettlementEvent(c: AppContext, event: RampSettlementEvent) {
  if (event.kind === "ignore") {
    return;
  }

  const repo = createSystemPaymentsRepository(c.env);
  const transfer = await repo.getTransferByProviderReference({
    provider: event.provider,
    providerReference: event.reference,
  });
  if (!transfer) {
    return;
  }
  if (!isRampTransferType(transfer.type)) {
    return;
  }
  // Out-of-order or redelivered events must not regress a settled transfer
  // (e.g. a retried PENDING arriving after COMPLETED).
  if (isTerminalRampTransferStatus(transfer.status)) {
    return;
  }

  const update: Parameters<typeof repo.updateTransferStatusGuarded>[0] = {
    transferId: transfer.id,
    organizationId: transfer.organization_id,
    projectId: transfer.project_id,
    fromStatuses: ALLOWED_RAMP_SETTLEMENT_SOURCE_STATUSES[event.kind],
    toStatus: RAMP_SETTLEMENT_STATUS[event.kind],
    updatedAt: new Date().toISOString(),
  };
  // Record the actual settled amount the provider reports: the fiat payout for
  // off-ramp, the delivered crypto for on-ramp.
  if (event.kind === "settled" && event.receivedAmount) {
    if (transfer.type === "offramp") {
      update.fiatAmount = event.receivedAmount;
    } else {
      update.amount = event.receivedAmount;
    }
  }
  if ((event.kind === "failed" || event.kind === "expired") && event.error) {
    update.error = event.error;
  }
  // Economics are captured only here, at the terminal settlement webhook — they are not
  // backfilled for transfers that settled before this shipped.
  if (
    (event.kind === "settled" || event.kind === "failed" || event.kind === "expired") &&
    event.settlement
  ) {
    update.providerData = { settlement: event.settlement };
  }

  await repo.updateTransferStatusGuarded(update);
}
