type PayuSuccessPayload = {
  status: string;
  txnid: string;
  amount: string;
  mihpayid: string;
  hash: string;
  firstname: string;
  email: string;
  phone: string;
  productinfo: string;
  udf1?: string;
  udf2?: string;
  udf3?: string;
  udf4?: string;
  udf5?: string;
};

type PayuCheckoutParams = {
  key: string;
  txnid: string;
  amount: string;
  productinfo: string;
  firstname: string;
  email: string;
  phone: string;
  surl: string;
  furl: string;
  hash: string;
  payu_base_url: string;
  udf1?: string;
  udf2?: string;
  udf3?: string;
  udf4?: string;
  udf5?: string;
};

type PayuCheckoutOptions = {
  amount: number;
  currency: string;
  txnId: string;
  description: string;
  firstname?: string;
  email?: string;
  phone?: string;
  udf1?: string;
  udf2?: string;
};

const PAYU_CHECKOUT_BUILD = (params: PayuCheckoutParams) => {
  const url = `${params.payu_base_url}/_payment`;
  const inputs: Array<[string, string]> = [
    ["key", params.key],
    ["txnid", params.txnid],
    ["amount", params.amount],
    ["productinfo", params.productinfo],
    ["firstname", params.firstname],
    ["email", params.email],
    ["phone", params.phone],
    ["surl", params.surl],
    ["furl", params.furl],
    ["hash", params.hash],
    ["udf1", params.udf1 || ""],
    ["udf2", params.udf2 || ""],
    ["udf3", params.udf3 || ""],
    ["udf4", params.udf4 || ""],
    ["udf5", params.udf5 || ""],
  ];
  return { url, inputs };
};

function ensureAbsoluteUrl(path: string): string {
  if (typeof window === "undefined") return path;
  if (/^https?:\/\//.test(path)) return path;
  const base = window.location.origin;
  return `${base}${path.startsWith("/") ? path : `/${path}`}`;
}

function readQueryFromCurrentUrl(): Record<string, string> {
  if (typeof window === "undefined") return {};
  const search = window.location.search.replace(/^\?/, "");
  if (!search) return {};
  return Object.fromEntries(new URLSearchParams(search));
}

// Submit an already-built PayU order (key/txnid/hash/etc. obtained from
// whichever backend endpoint created it -- `/api/payments/create-order`
// for TraceCoins, `/api/ai-credits/order` for AI credits) as a form POST
// to PayU, and resolve/reject based on the callback result. This is the
// shared checkout mechanics; it does NOT create an order itself -- the
// caller must have already done that. Use `openPayuCheckout` below if you
// need both steps for the TraceCoins flow specifically.
export async function submitPayuCheckout(
  rawParams: PayuCheckoutParams
): Promise<PayuSuccessPayload> {
  if (typeof window === "undefined") {
    throw new Error("PayU checkout is only available in the browser.");
  }

  const params: PayuCheckoutParams = {
    ...rawParams,
    surl: ensureAbsoluteUrl(rawParams.surl),
    furl: ensureAbsoluteUrl(rawParams.furl),
  };

  const { url, inputs } = PAYU_CHECKOUT_BUILD(params);

  return new Promise<PayuSuccessPayload>((resolve, reject) => {
    const form = document.createElement("form");
    form.method = "POST";
    form.action = url;
    form.style.display = "none";
    for (const [name, value] of inputs) {
      const input = document.createElement("input");
      input.type = "hidden";
      input.name = name;
      input.value = value;
      form.appendChild(input);
    }
    document.body.appendChild(form);

    let resolved = false;

    const onMessage = (event: MessageEvent) => {
      if (event.origin !== window.location.origin) return;
      if (!event.data || typeof event.data !== "object") return;
      const data = event.data as Record<string, unknown>;
      if (data.type === "payu:result" && data.payload) {
        resolved = true;
        window.removeEventListener("message", onMessage);
        const payload = data.payload as PayuSuccessPayload;
        if (String(payload.status || "").toLowerCase() === "success") {
          resolve(payload);
        } else {
          reject(new Error("Payment failed or was cancelled."));
        }
      }
    };

    window.addEventListener("message", onMessage);

    const start = Date.now();
    const poll = window.setInterval(() => {
      const query = readQueryFromCurrentUrl();
      if (query.txnid && query.txnid === params.txnid) {
        window.clearInterval(poll);
        if (!resolved) {
          resolved = true;
          window.removeEventListener("message", onMessage);
          const status = (query.status || "").toLowerCase();
          if (status === "success") {
            resolve({
              status: "SUCCESS",
              txnid: query.txnid,
              amount: query.amount || params.amount,
              mihpayid: query.mihpayid || "",
              hash: query.hash || "",
              firstname: params.firstname,
              email: params.email,
              phone: params.phone,
              productinfo: params.productinfo,
              udf1: params.udf1,
              udf2: params.udf2,
            });
          } else {
            reject(new Error("Payment failed or was cancelled."));
          }
        }
      } else if (Date.now() - start > 30 * 60 * 1000) {
        window.clearInterval(poll);
        if (!resolved) {
          resolved = true;
          window.removeEventListener("message", onMessage);
          reject(new Error("PayU checkout timed out."));
        }
      }
    }, 500);

    form.submit();
  });
}

// TraceCoins checkout: creates the order via `/api/payments/create-order`
// (using `options.txnId` as the tracecoin package id, per that endpoint's
// contract) and then submits it. For AI credits, do NOT use this --
// create the order via `/api/ai-credits/order` first and pass its
// response straight to `submitPayuCheckout` instead, since this function
// would otherwise create a second, mismatched order (see CreditsPage.tsx).
export async function openPayuCheckout(
  options: PayuCheckoutOptions
): Promise<PayuSuccessPayload> {
  if (typeof window === "undefined") {
    throw new Error("PayU checkout is only available in the browser.");
  }

  const orderRes = await fetch("/api/payments/create-order", {
    method: "POST",
    credentials: "include",
    headers: { "Content-Type": "application/json", Accept: "application/json" },
    body: JSON.stringify({
      amount: options.amount,
      currency: options.currency,
      package_id: options.txnId,
    }),
  });

  if (!orderRes.ok) {
    throw new Error("Failed to create PayU order.");
  }

  const orderData = await orderRes.json();
  return submitPayuCheckout({
    key: orderData.key,
    txnid: orderData.txnid,
    amount: orderData.amount,
    productinfo: orderData.productinfo,
    firstname: orderData.firstname,
    email: orderData.email,
    phone: orderData.phone,
    surl: orderData.surl,
    furl: orderData.furl,
    hash: orderData.hash,
    payu_base_url: orderData.payu_base_url,
    udf1: orderData.udf1,
    udf2: orderData.udf2,
    udf3: orderData.udf3,
    udf4: orderData.udf4,
    udf5: orderData.udf5,
  });
}

export type { PayuSuccessPayload, PayuCheckoutParams, PayuCheckoutOptions };
