import type { PriceUnit } from '@/services/catalog/types';
import { isTerminalBookingRequestStatus, type BookingRequestStatus } from '@/services/bookingRequests/types';

/**
 * Payment domain — the checkout/card-capture layer (b10) plus the customer invoice read (b11). Shapes
 * mirror the published contracts (`dev/contracts/domains/payments.md`, `refunds-invoices.md`; camelCase
 * wire, `clientFetch` unwraps the `ApiEnvelope<T>`).
 *
 * Load-bearing semantics (contracts + phase §5):
 *  - **Money is IRR integer, on the wire as a digit-string.** Never coerce to a JS number; parse with the
 *    `@/utils` BigInt helpers, display as Toman via `formatIrrToToman`. No float math on the money path.
 *  - **Payment is initiated against the accepted REQUEST, not a booking** — the `bookings` row is created
 *    & confirmed on capture (the PSP webhook), never on initiate.
 *  - **Idempotency is per attempt.** One stable `Idempotency-Key` (header) per payment attempt, reused
 *    across retries of that attempt; a NEW attempt gets a new key. A `409` on initiate means "already in
 *    progress / already captured" — a benign convergence, never an error toast.
 *  - **There is no client verify endpoint.** The server re-verifies inside the webhook handler; the client
 *    learns the outcome by polling (`getPaymentOutcome`). On the real path that poll maps the request
 *    status (`converted` → succeeded); a first-class transaction-status read is filed as REQ-017.
 *  - **VAT is on Balinyaar's commission only** (the platform's taxable supply) — never the nurse payout.
 */

/** `payment_transactions.status` (b10 contract enum): no other states cross the wire. */
export type PaymentTransactionStatus = 'pending' | 'succeeded' | 'failed';

/** `invoices.moadian_status` (b11) — the e-invoicing registration state, surfaced read-only. */
export type MoadianStatus = 'pending' | 'submitted' | 'registered' | 'failed';

/** What the dev gateway round-trip reports back (mock harness); the real PSP signals via the webhook. */
export type GatewayReturnOutcome = 'success' | 'failure';

/**
 * The C6 payload. **Not yet served by the backend** (REQ-016): the b8 `BookingRequestDto` is money-free
 * and no checkout-summary endpoint exists, so the mock builds this and the real `clientApi` targets the
 * proposed route until it lands. Display rows are **served, never client-derived** — in particular
 * `vatIrr` (the client must not derive tax with a float rate) — and reconcile by construction:
 * `serviceCostIrr + commissionIrr + vatIrr = totalIrr`, with `totalIrr = grossPriceIrr` and the b10
 * three-amount invariant `grossPriceIrr = balinyaarCommissionIrr + nursePayoutAmount`.
 */
export interface CheckoutSummaryDto {
  bookingRequestId: number;
  /** C6 renders only for `accepted_awaiting_payment`; other statuses get a convergence/terminal card. */
  requestStatus: BookingRequestStatus;
  nurseName: string;
  patientName: string;
  variantLabel: string;
  variantPriceUnit: PriceUnit;
  sessionCount: number;
  /** ISO date `YYYY-MM-DD`. */
  requestedDate: string;
  /** `HH:mm:ss`. */
  requestedTimeStart: string;
  requestedTimeEnd: string;
  /** Server-frozen UTC end of the 30-minute payment window. */
  paymentDeadlineAt: string | null;
  /** Display row: the nursing service itself (= the nurse payout leg). IRR digit-string. */
  serviceCostIrr: string;
  /** Display row: Balinyaar's commission **net of VAT**. IRR digit-string. */
  commissionIrr: string;
  /** Display row: VAT on the commission (the served figure — never derived client-side). */
  vatIrr: string;
  /** Snapshotted VAT rate (decimal, e.g. 0.1) — informational; the rial figure is `vatIrr`. */
  vatRate: number;
  /** Display total (= `grossPriceIrr`); the rows above sum to exactly this. */
  totalIrr: string;
  /** The b10 three-amount split (gross = commission + payout, guaranteed server-side). */
  grossPriceIrr: string;
  balinyaarCommissionIrr: string;
  nursePayoutAmount: string;
}

/** `POST bookings/{bookingRequestId}/payments` input — the key travels as the `Idempotency-Key` header. */
export interface InitiatePaymentInput {
  bookingRequestId: number;
  /** Stable per payment attempt (reused across retries of the same attempt); a new attempt gets a new one. */
  idempotencyKey: string;
}

/** `InitiatePaymentResult` (b10 swagger): where to send the customer, and the PSP reference. */
export interface InitiatePaymentResult {
  transactionId: number;
  /** Absolute PSP URL on the real path; an app-relative gateway-harness path from the mock. */
  redirectUrl: string | null;
  gatewayReferenceCode: string | null;
}

/**
 * What the return-from-gateway surface reports: the harness outcome plus which transaction came back.
 * On the real path the PSP has already hit the webhook before redirecting — the impl just re-reads the
 * outcome; in the mock this IS the capture trigger (the client stand-in for the webhook confirm).
 */
export interface ConfirmGatewayReturnInput {
  bookingRequestId: number;
  transactionId: number | null;
  outcome: GatewayReturnOutcome;
}

/**
 * The pending-callback poll target. No client transaction read exists in the b10 contract (REQ-017), so
 * the real path derives `transactionStatus` from the request status (`converted` → succeeded,
 * `payment_deadline_expired` → failed, else pending) and `bookingId` stays `null` until the backend
 * serves it; the mock knows both first-hand.
 */
export interface PaymentOutcomeDto {
  bookingRequestId: number;
  requestStatus: BookingRequestStatus;
  transactionStatus: PaymentTransactionStatus | null;
  /** The confirmed booking to link to (client-augmented; `null` on the real path until REQ-017 lands). */
  bookingId: number | null;
}

/** succeeded/failed transaction, or a request that left the payable state — nothing left to poll. */
export function isTerminalPaymentOutcome(outcome: PaymentOutcomeDto): boolean {
  return (
    outcome.transactionStatus === 'succeeded' ||
    outcome.transactionStatus === 'failed' ||
    isTerminalBookingRequestStatus(outcome.requestStatus)
  );
}

/** `InvoiceDto` (b11 swagger, `GET invoices/{bookingId}`) — flat totals; VAT is on the commission line only. */
export interface InvoiceDto {
  id: number;
  bookingId: number;
  /** Sequential, gap-free human-facing reference (e.g. `INV-0000000001`) — treat as opaque. */
  invoiceNumber: string;
  issuingEntityType: 'platform' | 'partner_center';
  grossIrr: string;
  /** Balinyaar's commission — the VAT-relevant line (`vatIrr = round(commission × vatRate)`). */
  platformCommissionIrr: string;
  bnplCommissionIrr: string | null;
  vatRate: number;
  vatIrr: string;
  moadianReferenceNumber: string | null;
  moadianStatus: MoadianStatus | null;
  pdfUrl: string | null;
  issuedAt: string;
}

/**
 * The payment API seam — the real HTTP client and the in-memory mock both implement this interface;
 * selection is by config (`USE_PAYMENT_MOCK`), never scattered `if (mock)` checks.
 */
export interface PaymentApi {
  getCheckoutSummary(bookingRequestId: number): Promise<CheckoutSummaryDto>;
  initiatePayment(input: InitiatePaymentInput): Promise<InitiatePaymentResult>;
  confirmGatewayReturn(input: ConfirmGatewayReturnInput): Promise<PaymentOutcomeDto>;
  getPaymentOutcome(bookingRequestId: number): Promise<PaymentOutcomeDto>;
  getInvoice(bookingId: number): Promise<InvoiceDto>;
}
