import type { BookingRequestStatus } from '@/services/bookingRequests/types';
import type { StatusKind } from '@/components/StatusChip/StatusChip';

/**
 * BNPL domain — the installment-checkout alternative to the f9 card flow (b12). Shapes mirror the
 * published contract (`dev/contracts/domains/bnpl.md`; camelCase wire, `clientFetch` unwraps the
 * `ApiEnvelope<T>`), extended with the D1–D5 read surfaces the contract does not yet serve (filed as
 * REQ-022/023/024 — see `apis/clientApi.ts`).
 *
 * The load-bearing product truth every type here encodes
 * ([product/business/09](../../../../product/business/09-installments-bnpl.md)):
 *  - **The installment repayment is OWNED BY THE PROVIDER, not Balinyaar.** The provider pays Balinyaar
 *    the full order amount up-front and bears 100% of the customer's default risk. In Balinyaar's books a
 *    settled BNPL order is **identical to a card payment that lands net-of-fee in one inbound settlement**.
 *  - So the Wallet view (D5) renders **provider-reported** status (`WalletInstallmentPlan`) — never a
 *    Balinyaar-managed ledger, and Balinyaar never settles a customer installment ("early pay" hands off
 *    to the provider).
 *  - **Money is IRR integer, on the wire as a digit-string.** Never coerce to a JS number; parse with the
 *    `@/utils` BigInt helpers and display as Toman via `formatIrrToToman`. The client renders whatever the
 *    contract returns — it never computes a fee, ceiling, monthly amount, or schedule amount itself
 *    (they are per-contract/per-provider config; the mock plays the server here).
 */

/** `bnpl_transactions.status` (b12 contract enum) — forward-only. */
export type BnplStatus = 'eligible' | 'token_issued' | 'verified' | 'settled' | 'reverted' | 'cancelled' | 'failed';

/** `bnpl_transactions.eligibility_status` (b12) — anything but `eligible` falls back to card. */
export type BnplEligibilityStatus = 'eligible' | 'not_eligible' | 'ceiling_exceeded';

/**
 * `provider_code` — selects the provider adapter. The b12 contract enum is
 * `snapppay | digipay | tara | torobpay`; `balinyaar` is the in-house plan the wireframe shows
 * (اقساط بالین‌یار) which the contract enum does not yet carry (flagged in REQ-022). Provider **display
 * names** are i18n keys off this code (`bnpl.provider_{code}`) — never derived from the code text.
 */
export type ProviderCode = 'snapppay' | 'digipay' | 'tara' | 'torobpay' | 'balinyaar';

/** What the provider handoff reports back on return (mock harness); the real provider signals via webhook. */
export type BnplHandoffOutcome = 'success' | 'failure';

/**
 * A single installment plan a provider offers (D2). **All amounts are served IRR digit-strings** — the
 * client renders them, it never computes a monthly amount, down-payment, or total. `feePercent` /
 * `downPaymentPercent` are informational fractions the plan card shows (`کارمزد ۴٪`, `پیش‌پرداخت ۲۰٪`).
 */
export interface BnplPlanOption {
  /** Stable id for single-select + the handoff (e.g. `digipay_6m`). */
  planId: string;
  /** 3/6/12 for monthly plans; `null` for an N-installment plan (اسنپ‌پی ۴ قسط). */
  termMonths: number | null;
  installmentCount: number;
  /** Provider fee as a fraction (0 = بدون سود / interest-free) — display only. */
  feePercent: number;
  /** Down-payment as a fraction of the total (0..1) — display only. */
  downPaymentPercent: number;
  /** Per-installment amount (IRR digit-string, served). */
  monthlyAmountIrr: string;
  /** The پیش‌پرداخت due today (IRR digit-string, served). */
  downPaymentIrr: string;
  /** What the customer repays in total across down-payment + installments (IRR digit-string, served). */
  totalIrr: string;
}

/** A provider and the plans it offers for this order (D1/D2). Rendered from the contract — never hardcoded. */
export interface BnplProvider {
  providerCode: ProviderCode;
  /** Provider-reported one-line plan headline (informational; the surrounding copy is i18n). */
  tagline: string | null;
  plans: BnplPlanOption[];
}

/**
 * The D1/D2 payload — the payable amount + the provider/plan set. `bnplEligible` is the §1 branch-note
 * flag: when `false` the installment options are hidden and only card shows (the mock always returns
 * `true` and lets D3 eligibility be the real gate).
 */
export interface BnplOptions {
  bookingRequestId: number;
  requestStatus: BookingRequestStatus;
  /** The payable gross (D1 «مبلغ قابل پرداخت») — IRR digit-string. */
  orderAmountIrr: string;
  bnplEligible: boolean;
  providers: BnplProvider[];
}

/** D3 credit-check inputs. National-ID validation is client-side format only (10 digits); the real check is the provider's. */
export interface CheckEligibilityInput {
  bookingRequestId: number;
  providerCode: ProviderCode;
  /** 10-digit national ID (کد ملی). */
  nationalId: string;
  /** Mobile, prefilled from the session. */
  mobile: string;
  /** Gates the submit — no consent, no request. */
  consent: boolean;
}

/** D3 result — the provider's verdict + (on approval) the usable credit ceiling. */
export interface BnplEligibilityResult {
  eligibilityStatus: BnplEligibilityStatus;
  isEligible: boolean;
  installmentCount: number;
  /** Provider-served plan summary (informational; may be `null`). */
  planSummary: string | null;
  /** سقف اعتبار — IRR digit-string, or `null` when declined. */
  creditCeilingIrr: string | null;
}

/** One row of the D4 repayment table / D5 due list. */
export interface BnplInstallmentRow {
  /** 0 = پیش‌پرداخت (down-payment), 1..N = قسط. */
  sequence: number;
  kind: 'down_payment' | 'installment';
  /** ISO date `YYYY-MM-DD` (Shamsi display is a client concern). The down-payment row renders «امروز». */
  dueDate: string;
  amountIrr: string;
  /** Provider-reported per-row status — present in D5 (Wallet), absent in the D4 preview. */
  status?: BnplInstallmentStatus;
}

/** The D4 repayment schedule — served, not client-derived (amounts + due dates are provider-owned). */
export interface BnplSchedule {
  bookingRequestId: number;
  providerCode: ProviderCode;
  planId: string;
  totalIrr: string;
  downPaymentIrr: string;
  installmentCount: number;
  /** `[down_payment, installment×N]`. */
  rows: BnplInstallmentRow[];
}

export interface GetBnplScheduleInput {
  bookingRequestId: number;
  providerCode: ProviderCode;
  planId: string;
}

/** D4 final action — start the BNPL order + issue the provider token/redirect (contract `initiate`). */
export interface IssueBnplTokenInput {
  bookingRequestId: number;
  providerCode: ProviderCode;
  planId: string;
  /** Stable per attempt (b12 `Idempotency-Key` header); a retried initiate reuses the same token. */
  idempotencyKey: string;
}

/** `initiate` result (b12) — where to hand the customer off, and the provider references. */
export interface IssueBnplTokenResult {
  bnplTransactionId: number;
  paymentTransactionId: number;
  status: BnplStatus;
  externalPaymentToken: string | null;
  /** Absolute provider URL on the real path; an app-relative provider-harness path from the mock. */
  redirectUrl: string | null;
}

/** The provider-return settle input (down-payment cleared). The real path settles via webhook; on return the client reads the order. */
export interface AcceptBnplScheduleInput {
  bookingRequestId: number;
  bnplTransactionId: number | null;
  outcome: BnplHandoffOutcome;
}

/**
 * What the return-from-provider surface reports: the order after the settle attempt, plus which booking
 * the settlement confirmed (client-augmented — the b12 contract sets `bookingId` at settle, but the
 * customer-readable link is filed as REQ-024). On success the booking confirms exactly as the card path.
 */
export interface BnplSettlementResult {
  bnplTransactionId: number;
  bookingRequestId: number;
  requestStatus: BookingRequestStatus;
  status: BnplStatus;
  bookingId: number | null;
  providerCode: ProviderCode;
}

/** `BnplOrderStatus` (b12 `GET checkout_bnpl/{id}`) — the customer's own order, read for the return poll. */
export interface BnplOrderStatus {
  id: number;
  paymentTransactionId: number;
  bookingId: number | null;
  providerCode: ProviderCode;
  status: BnplStatus;
  eligibilityStatus: BnplEligibilityStatus | null;
  orderAmountIrr: string;
  settledAmountIrr: string | null;
  bnplCommissionIrr: string | null;
  currency: string;
  installmentCount: number;
  /** Nullable — settlement is contract-defined and not instant. */
  settledAt: string | null;
  createdAt: string;
}

/** Provider-reported per-installment status (D5). */
export type BnplInstallmentStatus = 'paid' | 'due_soon' | 'upcoming' | 'overdue';

/**
 * The D5 Wallet view of an active installment plan — **provider-reported status, not a Balinyaar ledger**.
 * The contract models none of this (installment_count is informational; the repayment schedule is
 * explicitly out of scope), so the whole shape is a client/REQ-024 gap the mock fills.
 */
export interface WalletInstallmentPlan {
  bnplTransactionId: number;
  bookingId: number | null;
  providerCode: ProviderCode;
  status: BnplStatus;
  /** Human context for the plan (e.g. the service/nurse the order paid for). */
  serviceLabel: string;
  /** مانده بدهی — IRR digit-string. */
  outstandingBalanceIrr: string;
  installmentCount: number;
  /** قسط بعدی — ISO date, or `null` when nothing is outstanding. */
  nextDueDate: string | null;
  nextAmountIrr: string | null;
  /** Provider-reported due list with per-row status. */
  installments: BnplInstallmentRow[];
  /** Hand-off link for پرداخت زودهنگام — early-pay is a **provider** action, never a Balinyaar transaction. */
  earlyPayUrl: string | null;
  createdAt: string;
}

/** Forward-only terminal check for the return poll. */
const TERMINAL_BNPL_STATUSES: readonly BnplStatus[] = ['settled', 'reverted', 'cancelled', 'failed'];

export function isTerminalBnplStatus(status: BnplStatus): boolean {
  return TERMINAL_BNPL_STATUSES.includes(status);
}

/** A settled order (or a converted request) — the booking confirmed; nothing left to poll on the return. */
export function isBnplSettlementSuccess(result: BnplSettlementResult): boolean {
  return result.status === 'settled' || result.requestStatus === 'converted';
}

/** Maps a provider-reported installment status to the shared StatusChip kind (D5 due list). */
export function installmentStatusKind(status: BnplInstallmentStatus): StatusKind {
  switch (status) {
    case 'paid':
      return 'verified';
    case 'due_soon':
      return 'pending';
    case 'overdue':
      return 'rejected';
    case 'upcoming':
    default:
      return 'neutral';
  }
}

/**
 * The BNPL API seam — the real HTTP client and the in-memory mock both implement this interface;
 * selection is by config (`USE_BNPL_MOCK`), never scattered `if (mock)` checks.
 */
export interface BnplApi {
  getBnplOptions(bookingRequestId: number): Promise<BnplOptions>;
  checkEligibility(input: CheckEligibilityInput): Promise<BnplEligibilityResult>;
  getBnplSchedule(input: GetBnplScheduleInput): Promise<BnplSchedule>;
  issueBnplToken(input: IssueBnplTokenInput): Promise<IssueBnplTokenResult>;
  acceptBnplSchedule(input: AcceptBnplScheduleInput): Promise<BnplSettlementResult>;
  getBnplOrder(bookingRequestId: number): Promise<BnplOrderStatus>;
  getWalletInstallments(): Promise<WalletInstallmentPlan[]>;
}
