import { clientFetch } from '@/lib/api/client';
import { unwrap, type ApiEnvelope } from '@/lib/api/types';
import type {
  AcceptBnplScheduleInput,
  BnplApi,
  BnplEligibilityResult,
  BnplOptions,
  BnplOrderStatus,
  BnplSchedule,
  BnplSettlementResult,
  CheckEligibilityInput,
  GetBnplScheduleInput,
  IssueBnplTokenInput,
  IssueBnplTokenResult,
  WalletInstallmentPlan,
} from '../types';

const CHECKOUT_BNPL = '/api/v1/checkout_bnpl';

/** The header b12's `initiate` reads the per-attempt idempotency key from. */
const IDEMPOTENCY_KEY_HEADER = 'Idempotency-Key';

/**
 * Real HTTP implementation of the `BnplApi` seam. `checkEligibility` and `issueBnplToken` map the published
 * b12 contract 1:1, and the order read **by its own id** (`GET checkout_bnpl/{id}`) is contract-correct; the
 * rest cover contract gaps the frontend filed:
 *  - `getBnplOptions` (D1/D2 provider+plan list, per-plan monthly/down-payment/total) — **REQ-022**: the
 *    contract serves no options endpoint. Targets a proposed slug; 404s until it lands.
 *  - `getBnplSchedule` (D4 repayment table) — **REQ-022**: the contract explicitly does not model the
 *    customer's repayment schedule. Targets a proposed slug; 404s until it lands.
 *  - `acceptBnplSchedule` (settle-on-return) — the real path settles inside the provider **webhook**
 *    (the client is never trusted), so on return there is nothing to submit; it reads the order status and
 *    maps it (`settled` → success, `converted` request → success). **REQ-024** asks for a customer-readable
 *    `bookingId` on the order.
 *  - `getWalletInstallments` (D5 provider-reported status) — **REQ-024**: no wallet/installment-status read
 *    exists (the repayment schedule is provider-owned and out of the contract's scope). Targets a proposed
 *    slug; 404s until it lands.
 *  - `getBnplOrder(bookingRequestId)` (the return poll) — **REQ-024**: `GET checkout_bnpl/{id}` is keyed by
 *    the ORDER id, not the request id, and there is no customer *by-request* order lookup; the poll holds
 *    the request id, not the order id. Targets a proposed by-request slug; 404s until it lands. (The
 *    settle-on-return path below reads the order **by its own id**, which IS contract-correct.)
 *
 * NOT the primary implementation this phase (`USE_BNPL_MOCK = true`) — see `constants.ts` for why.
 */

/** Read a BNPL order by its own id — the contract-correct `GET checkout_bnpl/{id}` (id = bnplTransactionId). */
const readOrderById = async (orderId: number): Promise<BnplOrderStatus> =>
  unwrap(await clientFetch<ApiEnvelope<BnplOrderStatus>>(`${CHECKOUT_BNPL}/${orderId}`));

export const bnplClientApi: BnplApi = {
  getBnplOptions: async (bookingRequestId: number) =>
    // REQ-022 proposed slug (owner-scoped). 404s until the backend delivers it — the domain stays mock-primary.
    unwrap(await clientFetch<ApiEnvelope<BnplOptions>>(`${CHECKOUT_BNPL}/options/${bookingRequestId}`)),

  checkEligibility: async ({ bookingRequestId, providerCode, nationalId, mobile, consent }: CheckEligibilityInput) =>
    unwrap(
      await clientFetch<ApiEnvelope<BnplEligibilityResult>>(`${CHECKOUT_BNPL}/eligibility`, {
        method: 'POST',
        // The contract body is `{ bookingRequestId, providerCode }`; the D3 KYC inputs (nationalId/mobile/
        // consent) are sent for the provider credit check (REQ-023 — the contract eligibility does not yet
        // accept them). Extra fields are ignored server-side until the KYC step is defined.
        body: JSON.stringify({ bookingRequestId, providerCode, nationalId, mobile, consent }),
      }),
    ),

  getBnplSchedule: async ({ bookingRequestId, providerCode, planId }: GetBnplScheduleInput) =>
    // REQ-022 proposed slug. 404s until the backend serves the repayment schedule.
    unwrap(
      await clientFetch<ApiEnvelope<BnplSchedule>>(
        `${CHECKOUT_BNPL}/schedule/${bookingRequestId}?provider=${providerCode}&plan=${planId}`,
      ),
    ),

  issueBnplToken: async ({ bookingRequestId, providerCode, idempotencyKey }: IssueBnplTokenInput) =>
    unwrap(
      await clientFetch<ApiEnvelope<IssueBnplTokenResult>>(`${CHECKOUT_BNPL}/initiate`, {
        method: 'POST',
        headers: { [IDEMPOTENCY_KEY_HEADER]: idempotencyKey },
        body: JSON.stringify({ bookingRequestId, providerCode }),
      }),
    ),

  acceptBnplSchedule: async ({ bookingRequestId, bnplTransactionId }: AcceptBnplScheduleInput) => {
    // The provider settled via webhook before redirecting back — nothing to submit; read the order by its
    // OWN id (`GET checkout_bnpl/{id}`, carried back as `transaction_id`). Fall back to the by-request read
    // only if the order id wasn't carried through the handoff.
    const order =
      bnplTransactionId != null ? await readOrderById(bnplTransactionId) : await bnplClientApi.getBnplOrder(bookingRequestId);
    const result: BnplSettlementResult = {
      bnplTransactionId: order.id,
      bookingRequestId,
      // REQ-024: the b12 order gives no request status; the return page falls back to the order status.
      requestStatus: order.status === 'settled' ? 'converted' : 'accepted_awaiting_payment',
      status: order.status,
      bookingId: order.bookingId,
      providerCode: order.providerCode,
    };
    return result;
  },

  getBnplOrder: async (bookingRequestId: number) =>
    // REQ-024 gap (see header): b12 has no customer *by-request* order lookup; the poll needs one. Proposed slug.
    unwrap(await clientFetch<ApiEnvelope<BnplOrderStatus>>(`${CHECKOUT_BNPL}/by_request/${bookingRequestId}`)),

  getWalletInstallments: async () =>
    // REQ-024 proposed slug (owner-scoped, provider-reported). 404s until the backend serves it.
    unwrap(await clientFetch<ApiEnvelope<WalletInstallmentPlan[]>>(`${CHECKOUT_BNPL}/wallet_installments`)),
};
