import { clientFetch } from '@/lib/api/client';
import { unwrap, type ApiEnvelope } from '@/lib/api/types';
import type { BookingRequestDto } from '@/services/bookingRequests/types';
import type {
  CheckoutSummaryDto,
  ConfirmGatewayReturnInput,
  InitiatePaymentInput,
  InitiatePaymentResult,
  InvoiceDto,
  PaymentApi,
  PaymentOutcomeDto,
} from '../types';

const BOOKINGS = '/api/v1/bookings';
const BOOKING_REQUESTS = '/api/v1/booking_requests';
const INVOICES = '/api/v1/invoices';

/** The header b10's initiate (and b12's BNPL initiate) read the per-attempt idempotency key from. */
const IDEMPOTENCY_KEY_HEADER = 'Idempotency-Key';

/**
 * Real HTTP implementation of the `PaymentApi` seam. `initiatePayment` and `getInvoice` map the
 * published b10/b11 contracts 1:1. The other two calls cover contract gaps the frontend filed:
 *  - `getCheckoutSummary` targets the **REQ-016 proposed route** (no checkout-summary endpoint exists;
 *    the b8 request DTO is money-free, and the client must never derive commission/VAT itself).
 *  - `getPaymentOutcome`/`confirmGatewayReturn` poll the existing `booking_requests/get/{id}` and map its
 *    status (`converted` → succeeded, `payment_deadline_expired` → failed, else pending) — the b10
 *    contract has no client transaction read (REQ-017); the server verifies captures inside the webhook
 *    handler, so on return from the PSP there is nothing to "verify" client-side, only an outcome to read.
 *
 * NOT the primary implementation this phase (`USE_PAYMENT_MOCK = true`) — see `constants.ts` for why.
 */
export const paymentClientApi: PaymentApi = {
  getCheckoutSummary: async (bookingRequestId: number) =>
    // REQ-016 proposed slug (action-style, mirrors b8's `[controller]/[action]` routing). 404s until the
    // backend delivers it — which is why the domain stays mock-primary.
    unwrap(
      await clientFetch<ApiEnvelope<CheckoutSummaryDto>>(
        `${BOOKING_REQUESTS}/checkout_summary/${bookingRequestId}`,
      ),
    ),

  initiatePayment: async ({ bookingRequestId, idempotencyKey }: InitiatePaymentInput) =>
    unwrap(
      await clientFetch<ApiEnvelope<InitiatePaymentResult>>(`${BOOKINGS}/${bookingRequestId}/payments`, {
        method: 'POST',
        headers: { [IDEMPOTENCY_KEY_HEADER]: idempotencyKey },
      }),
    ),

  confirmGatewayReturn: async ({ bookingRequestId }: ConfirmGatewayReturnInput) =>
    // The PSP hit the webhook before redirecting back — there is nothing to submit; read the outcome.
    paymentClientApi.getPaymentOutcome(bookingRequestId),

  getPaymentOutcome: async (bookingRequestId: number) => {
    const request = unwrap(
      await clientFetch<ApiEnvelope<Omit<BookingRequestDto, 'variantPrice' | 'bookingId'>>>(
        `${BOOKING_REQUESTS}/get/${bookingRequestId}`,
      ),
    );
    const outcome: PaymentOutcomeDto = {
      bookingRequestId: request.id,
      requestStatus: request.status,
      transactionStatus:
        request.status === 'converted'
          ? 'succeeded'
          : request.status === 'payment_deadline_expired'
            ? 'failed'
            : 'pending',
      // REQ-017: the b8 DTO gives no way to reach the converted booking — the confirmation falls back
      // to the bookings list until the field lands.
      bookingId: null,
    };
    return outcome;
  },

  getInvoice: async (bookingId: number) =>
    unwrap(await clientFetch<ApiEnvelope<InvoiceDto>>(`${INVOICES}/${bookingId}`)),
};
