/**
 * Flitt subscriptions client — fastkit billing-flitt module.
 *
 * Flitt bills recurring charges itself: we open a hosted checkout with
 * `subscription: "Y"` + `recurring_data`, and Flitt re-charges the saved card
 * on the schedule until the subscription is stopped. Each charge is confirmed
 * by a signed server callback that keeps our `subscriptions` row in sync.
 *
 *   1. POST the subscription order to https://pay.flitt.com/api/checkout/url
 *   2. Redirect the customer to the returned checkout_url
 *   3. Flitt POSTs the (signed) result to our callback — verify, then activate
 *   4. Cancel via POST https://pay.flitt.com/api/subscription {action:"stop"}
 *
 * Signing is SHA-1 over `secret|<values sorted alphabetically by key>`, empty
 * values dropped ("0" kept), lowercase hex. IMPORTANT: `recurring_data` is a
 * nested object in the JSON body, but for the signature it must be serialised
 * as a Python-style dict string in a fixed field order (see
 * formatRecurringDataForSignature). This mirrors Flitt's own canonical form.
 *
 * Test mode works with Flitt's public test credentials (merchant 1549901 /
 * key "test"); see https://docs.flitt.com/api/testing/ for test cards.
 */
import { createHash } from 'crypto';

const FLITT_BASE = 'https://pay.flitt.com/api';

export function flittConfigured(): boolean {
  return process.env.FLITT_DISABLED !== '1';
}

export function flittMerchantId(): number {
  return Number(process.env.FLITT_MERCHANT_ID || '1549901');
}

export function flittSecretKey(): string {
  return process.env.FLITT_SECRET_KEY || 'test';
}

/** Public origin used for response_url / server_callback_url. */
export function publicBaseUrl(): string {
  return (process.env.PUBLIC_BASE_URL || process.env.APP_URL || 'http://localhost:5173').replace(/\/$/, '');
}

/**
 * SHA-1 signature: sha1(secret|<sorted, non-empty values joined by |>).
 * Drops empty strings (keeps "0"), excludes signature/response_signature_string.
 */
export function flittSignature(
  params: Record<string, string | number | boolean | null | undefined>,
  secretKey: string = flittSecretKey(),
): string {
  const values = Object.entries(params)
    .filter(([k]) => k !== 'signature' && k !== 'response_signature_string')
    .filter(([, v]) => v !== null && v !== undefined && String(v) !== '')
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([, v]) => String(v));
  return createHash('sha1').update([secretKey, ...values].join('|'), 'utf8').digest('hex');
}

/** Verify the signature on a Flitt callback/response payload. */
export function verifyCallback(payload: Record<string, unknown>): boolean {
  const received = payload?.signature;
  if (typeof received !== 'string' || !received) return false;
  const expected = flittSignature(payload as Record<string, string | number>, flittSecretKey());
  return received === expected;
}

interface RecurringData {
  every: number;
  period: 'day' | 'week' | 'month';
  amount: number; // minor units
  state: string; // "hidden"
  readonly: string; // "Y"
  quantity: number;
}

/**
 * Serialise recurring_data for the signature exactly as Flitt's server does:
 * a Python-repr dict of the SAME keys sent in the JSON body, in the SAME
 * insertion order, with EVERY value quoted as a string (numbers included) —
 * e.g. {'every': '1', 'period': 'month', 'amount': '900', …}. This string is
 * then placed at the `recurring_data` slot in the top-level sorted signature.
 *
 * Verified against Flitt's documented golden vector (merchant 1549901 / "test"
 * → 4120ca8501b8003bb4860c6fc354c9e3ceb85d0c). Key order is load-bearing: the
 * object you pass here MUST be the exact object you send in the request body,
 * so both sides serialise identically.
 */
function formatRecurringDataForSignature(rd: RecurringData): string {
  const parts = Object.entries(rd).map(([k, v]) => `'${k}': '${v}'`);
  return `{${parts.join(', ')}}`;
}

interface FlittResponse {
  response_status?: string;
  error_message?: string;
  checkout_url?: string;
  payment_id?: string | number;
  order_status?: string;
  rectoken?: string;
  rectoken_lifetime?: string;
  [key: string]: unknown;
}

async function flittPost(path: string, body: Record<string, unknown>): Promise<FlittResponse> {
  const res = await fetch(`${FLITT_BASE}${path}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ request: body }),
    signal: AbortSignal.timeout(15_000),
  });
  const data = (await res.json().catch(() => ({}))) as { response?: FlittResponse };
  return data?.response ?? (data as FlittResponse);
}

/**
 * Open a hosted subscription checkout. `amountMinor` is the recurring charge in
 * minor units (integer). `every` is the count of `period` between charges
 * (e.g. every:1 period:"month" = monthly; every:12 period:"month" = yearly).
 * Returns the Flitt response; on success it contains checkout_url.
 */
export async function createSubscriptionCheckout(input: {
  orderId: string;
  amountMinor: number;
  currency: string;
  description: string;
  every: number;
  period?: 'day' | 'week' | 'month';
  email?: string;
  /** Opaque data echoed back on the callback (we store user/plan/interval). */
  merchantData?: Record<string, unknown>;
  baseUrl?: string;
}): Promise<FlittResponse> {
  const base = (input.baseUrl || publicBaseUrl()).replace(/\/$/, '');
  const recurring: RecurringData = {
    every: input.every,
    period: input.period || 'month',
    amount: input.amountMinor,
    state: 'hidden',
    readonly: 'Y',
    quantity: 9999,
  };

  // Values that participate in the signature. recurring_data uses its
  // Python-dict string form here (NOT the JSON object below).
  const signed: Record<string, string | number> = {
    merchant_id: flittMerchantId(),
    order_id: input.orderId,
    order_desc: input.description.slice(0, 1024),
    amount: input.amountMinor,
    currency: input.currency,
    version: '1.0.1',
    subscription: 'Y',
    required_rectoken: 'Y',
    recurring_data: formatRecurringDataForSignature(recurring),
    response_url: `${base}/api/billing/return?order_id=${input.orderId}`,
    server_callback_url: `${base}/api/billing/callback`,
    ...(input.email ? { sender_email: input.email } : {}),
    ...(input.merchantData ? { merchant_data: JSON.stringify(input.merchantData) } : {}),
  };

  const signature = flittSignature(signed);

  // The wire body carries recurring_data as a real nested object.
  const body: Record<string, unknown> = { ...signed, recurring_data: recurring, signature };
  return flittPost('/checkout/url', body);
}

/** Stop future charges on a subscription (POST /api/subscription action=stop). */
export async function cancelSubscription(orderId: string): Promise<FlittResponse> {
  // NOTE: the /subscription endpoint signs ONLY {action, merchant_id, order_id}
  // — including `version` makes Flitt reject the signature (verified live).
  const signed = {
    action: 'stop',
    merchant_id: flittMerchantId(),
    order_id: orderId,
  } satisfies Record<string, string | number>;
  return flittPost('/subscription', { ...signed, signature: flittSignature(signed) });
}

/** Ask Flitt for the authoritative status of an order. */
export async function orderStatus(orderId: string): Promise<FlittResponse> {
  const signed = {
    merchant_id: flittMerchantId(),
    order_id: orderId,
    version: '1.0.1',
  } satisfies Record<string, string | number>;
  return flittPost('/status/order_id', { ...signed, signature: flittSignature(signed) });
}

/**
 * Advanced / optional: charge a saved card yourself via /api/recurring using a
 * previously returned rectoken. Only needed if you disable Flitt-managed
 * recurring (state:"hidden") and drive renewals from your own scheduler.
 */
export async function chargeWithToken(input: {
  orderId: string;
  amountMinor: number;
  currency: string;
  description: string;
  rectoken: string;
}): Promise<FlittResponse> {
  const signed = {
    merchant_id: flittMerchantId(),
    order_id: input.orderId,
    order_desc: input.description.slice(0, 1024),
    amount: input.amountMinor,
    currency: input.currency,
    rectoken: input.rectoken,
    version: '1.0.1',
  } satisfies Record<string, string | number>;
  return flittPost('/recurring', { ...signed, signature: flittSignature(signed) });
}

/** A Flitt callback is a successful, approved payment. */
export function isApproved(payload: Record<string, unknown>): boolean {
  return payload?.response_status === 'success' && payload?.order_status === 'approved';
}
