/**
 * x402 CRDT payment — client-side helpers.
 *
 * Creates CRDT payment headers and wraps fetch to auto-handle
 * 402 responses with CRDT scheme when available.
 */

export interface CrdtPaymentParams {
  jwtToken: string;
  fromDid: string;
  fromLabel?: string;
  amount: number;
  tokenId: string;
}

/**
 * Create a base64-encoded X-PAYMENT header for CRDT scheme.
 */
export function createCrdtPaymentHeader(params: CrdtPaymentParams): string {
  const payload = {
    scheme: "crdt" as const,
    jwtToken: params.jwtToken,
    fromDid: params.fromDid,
    fromLabel: params.fromLabel,
    amount: params.amount,
    tokenId: params.tokenId,
    nonce: crypto.randomUUID(),
    timestamp: Date.now(),
  };

  const json = JSON.stringify(payload);
  return typeof btoa === "function"
    ? btoa(json)
    : Buffer.from(json).toString("base64");
}

interface AuthProvider {
  getToken: () => string | null;
  getDid: () => string | null;
  getLabel?: () => string | null;
  getBalance?: (tokenId: string) => number;
}

/**
 * Wrap fetch to auto-handle 402 responses with CRDT payment.
 *
 * On 402: checks if "crdt" scheme is offered and user has
 * sufficient balance → auto-retries with CRDT payment header.
 * Falls through if no CRDT option or insufficient balance.
 */
type FetchFn = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;

export function wrapFetchWithCrdtPayment(
  baseFetch: FetchFn,
  auth: AuthProvider,
): FetchFn {
  return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
    const response = await baseFetch(input, init);

    if (response.status !== 402) return response;

    // Check if we have auth
    const token = auth.getToken();
    const did = auth.getDid();
    if (!token || !did) return response;

    // Parse 402 response for CRDT option
    let body: any;
    try {
      body = await response.clone().json();
    } catch {
      return response;
    }

    const requirements = body.paymentRequirements;
    if (!Array.isArray(requirements)) return response;

    const crdtReq = requirements.find((r: any) => r.scheme === "crdt");
    if (!crdtReq) return response;

    const amount = parseInt(crdtReq.maxAmountRequired, 10);
    const tokenId = crdtReq.tokenId;

    // Check balance if provider available
    if (auth.getBalance) {
      const balance = auth.getBalance(tokenId);
      if (balance < amount) return response; // Insufficient — return original 402
    }

    // Create CRDT payment header and retry
    const paymentHeader = createCrdtPaymentHeader({
      jwtToken: token,
      fromDid: did,
      fromLabel: auth.getLabel?.() || undefined,
      amount,
      tokenId,
    });

    return baseFetch(input, {
      ...init,
      headers: {
        ...(init?.headers || {}),
        "X-PAYMENT": paymentHeader,
      },
    });
  };
}
