import { openStore } from "@/lib/data/store";

export type CheckoutStart = {
  ok: boolean;
  /** Where to send the person to pay. */
  url?: string;
  /** The gateway's own id for this attempt, kept so the webhook can match it. */
  reference?: string;
  error?: string;
};

/** The extra gateways, beyond Stripe and PayPal which have their own routes. */
export const EXTRA_GATEWAYS = [
  "razorpay",
  "paystack",
  "flutterwave",
  "mercadopago",
  "authorizeNet",
  "coinbase",
  "coinpayments",
] as const;

export type ExtraGateway = (typeof EXTRA_GATEWAYS)[number];

/** Which are switched on and configured, so the wallet can offer them. */
export async function availableGateways(): Promise<ExtraGateway[]> {
  const store = await openStore();
  const s = await store.settings();

  return EXTRA_GATEWAYS.filter((name) => {
    if (s[`${name}Enabled`] !== true) return false;
    switch (name) {
      case "razorpay":
        return Boolean(s.razorpayKeyId && s.razorpayKeySecret);
      case "paystack":
        return Boolean(s.paystackSecret);
      case "flutterwave":
        return Boolean(s.flutterwaveSecretKey);
      case "mercadopago":
        return Boolean(s.mercadopagoAccessToken);
      case "authorizeNet":
        return Boolean(s.authorizeNetLoginId && s.authorizeNetTransactionKey);
      case "coinbase":
        return Boolean(s.coinbaseApiKey);
      case "coinpayments":
        return Boolean(s.coinpaymentsMerchantId);
      default:
        return false;
    }
  });
}

/**
 * Starts a payment with one of the extra gateways.
 *
 * Each returns a URL to send the person to. The wallet is only credited
 * once the gateway confirms it, never from the browser.
 */
export async function startCheckout(opts: {
  gateway: ExtraGateway;
  amount: number;
  currency: string;
  email: string;
  reference: string;
  returnUrl: string;
}): Promise<CheckoutStart> {
  const store = await openStore();
  const s = await store.settings();
  const { gateway, amount, currency, email, reference, returnUrl } = opts;

  try {
    switch (gateway) {
      case "razorpay": {
        const id = String(s.razorpayKeyId);
        const secret = String(s.razorpayKeySecret);
        const res = await fetch("https://api.razorpay.com/v1/payment_links", {
          method: "POST",
          headers: {
            Authorization: `Basic ${Buffer.from(`${id}:${secret}`).toString("base64")}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            // Razorpay works in the smallest unit.
            amount: Math.round(amount * 100),
            currency,
            reference_id: reference,
            callback_url: returnUrl,
            callback_method: "get",
            customer: { email },
          }),
        });
        if (!res.ok) return { ok: false, error: "Razorpay refused the payment" };
        const d = await res.json();
        return { ok: true, url: d.short_url, reference: d.id };
      }

      case "paystack": {
        const res = await fetch("https://api.paystack.co/transaction/initialize", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${String(s.paystackSecret)}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            email,
            amount: Math.round(amount * 100),
            currency,
            reference,
            callback_url: returnUrl,
          }),
        });
        if (!res.ok) return { ok: false, error: "Paystack refused the payment" };
        const d = await res.json();
        return { ok: true, url: d.data?.authorization_url, reference: d.data?.reference };
      }

      case "flutterwave": {
        const res = await fetch("https://api.flutterwave.com/v3/payments", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${String(s.flutterwaveSecretKey)}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            tx_ref: reference,
            amount,
            currency,
            redirect_url: returnUrl,
            customer: { email },
          }),
        });
        if (!res.ok) return { ok: false, error: "Flutterwave refused the payment" };
        const d = await res.json();
        return { ok: true, url: d.data?.link, reference };
      }

      case "mercadopago": {
        const res = await fetch("https://api.mercadopago.com/checkout/preferences", {
          method: "POST",
          headers: {
            Authorization: `Bearer ${String(s.mercadopagoAccessToken)}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            items: [{ title: "Wallet top-up", quantity: 1, unit_price: amount, currency_id: currency }],
            payer: { email },
            external_reference: reference,
            back_urls: { success: returnUrl, failure: returnUrl },
          }),
        });
        if (!res.ok) return { ok: false, error: "Mercado Pago refused the payment" };
        const d = await res.json();
        return { ok: true, url: d.init_point, reference: d.id };
      }

      case "coinbase": {
        const res = await fetch("https://api.commerce.coinbase.com/charges", {
          method: "POST",
          headers: {
            "X-CC-Api-Key": String(s.coinbaseApiKey),
            "X-CC-Version": "2018-03-22",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            name: "Wallet top-up",
            description: `Top up ${currency} ${amount}`,
            pricing_type: "fixed_price",
            local_price: { amount: amount.toFixed(2), currency },
            metadata: { reference, email },
            redirect_url: returnUrl,
          }),
        });
        if (!res.ok) return { ok: false, error: "Coinbase refused the payment" };
        const d = await res.json();
        return { ok: true, url: d.data?.hosted_url, reference: d.data?.code };
      }

      case "coinpayments": {
        // CoinPayments posts to a hosted form rather than an API for this.
        const params = new URLSearchParams({
          cmd: "_pay_simple",
          reset: "1",
          merchant: String(s.coinpaymentsMerchantId),
          item_name: "Wallet top-up",
          currency,
          amountf: amount.toFixed(2),
          custom: reference,
          email,
          success_url: returnUrl,
          cancel_url: returnUrl,
        });
        return {
          ok: true,
          url: `https://www.coinpayments.net/index.php?${params}`,
          reference,
        };
      }

      case "authorizeNet": {
        const live = "https://api.authorize.net/xml/v1/request.api";
        const res = await fetch(live, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            getHostedPaymentPageRequest: {
              merchantAuthentication: {
                name: String(s.authorizeNetLoginId),
                transactionKey: String(s.authorizeNetTransactionKey),
              },
              transactionRequest: {
                transactionType: "authCaptureTransaction",
                amount: amount.toFixed(2),
              },
              hostedPaymentSettings: {
                setting: [
                  {
                    settingName: "hostedPaymentReturnOptions",
                    settingValue: JSON.stringify({ url: returnUrl, cancelUrl: returnUrl }),
                  },
                ],
              },
            },
          }),
        });
        if (!res.ok) return { ok: false, error: "Authorize.net refused the payment" };
        // Their response has a BOM that breaks JSON.parse.
        const text = (await res.text()).replace(/^\uFEFF/, "");
        const d = JSON.parse(text);
        if (!d.token) return { ok: false, error: "Authorize.net didn't return a token" };
        return {
          ok: true,
          url: `https://accept.authorize.net/payment/payment?token=${encodeURIComponent(d.token)}`,
          reference,
        };
      }

      default:
        return { ok: false, error: "Unknown gateway" };
    }
  } catch {
    return { ok: false, error: `Couldn't reach ${gateway}` };
  }
}
