import axios from "axios";
import { parse } from "node-html-parser";

import { paymentConfig } from "$config/index.js";

const xmlValue = (root, selector) => {
  const value = root.querySelector(selector.toLowerCase())?.text;
  return value === undefined ? null : String(value).trim();
};

const normalizeUrl = (value) => String(value || "").replace(/\?wsdl$/i, "");

const postSoap = async ({ url, soapAction, bodyXml }) => {
  const envelope = `<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>${bodyXml}</soap:Body>
</soap:Envelope>`;

  const { data } = await axios.post(normalizeUrl(url), envelope, {
    timeout: 20000,
    headers: {
      "Content-Type": "text/xml; charset=utf-8",
      SOAPAction: soapAction,
    },
  });

  return {
    raw: data,
    parsed: parse(String(data || "")),
  };
};

export const salePayment = async (payload) => {
  const xmlns = paymentConfig.parsianConfig.saleXmlns;
  const body = `<SalePaymentRequest xmlns="${escapeXml(xmlns)}">
  <requestData>
    <LoginAccount>${escapeXml(payload.LoginAccount)}</LoginAccount>
    <OrderId>${escapeXml(payload.OrderId)}</OrderId>
    <Amount>${Number(payload.Amount)}</Amount>
    <CallBackUrl>${escapeXml(payload.CallBackUrl)}</CallBackUrl>
    <AdditionalData>${escapeXml(payload.AdditionalData || "")}</AdditionalData>
    <Originator>${escapeXml(payload.Originator || payload.OrderId)}</Originator>
  </requestData>
</SalePaymentRequest>`;

  const result = await postSoap({
    url: paymentConfig.parsianConfig.saleUrl,
    soapAction: paymentConfig.parsianConfig.saleSoapAction,
    bodyXml: body,
  });

  return {
    status: Number(xmlValue(result.parsed, "Status") ?? -1),
    token: xmlValue(result.parsed, "Token"),
    message: xmlValue(result.parsed, "Message") || "",
    raw: result.raw,
    request: payload,
  };
};

export const confirmPayment = async ({ loginAccount, token }) => {
  const xmlns = paymentConfig.parsianConfig.confirmXmlns;
  const body = `<ConfirmPayment xmlns="${escapeXml(xmlns)}">
  <requestData>
    <LoginAccount>${escapeXml(loginAccount)}</LoginAccount>
    <Token>${escapeXml(token)}</Token>
  </requestData>
</ConfirmPayment>`;

  const result = await postSoap({
    url: paymentConfig.parsianConfig.confirmUrl,
    soapAction: paymentConfig.parsianConfig.confirmSoapAction,
    bodyXml: body,
  });

  return {
    status: Number(xmlValue(result.parsed, "Status") ?? -1),
    rrn: xmlValue(result.parsed, "RRN"),
    card:
      xmlValue(result.parsed, "CardNumberMasked") ||
      xmlValue(result.parsed, "HashCardNumber"),
    raw: result.raw,
    request: { loginAccount, token },
  };
};

export const redirectUrl = (token) => {
  const configuredUrl = String(paymentConfig.parsianConfig.gatewayUrl || "");
  if (configuredUrl.includes("token=")) {
    return `${configuredUrl}${encodeURIComponent(token)}`;
  }

  const base = configuredUrl.replace(/\/?$/, "/");
  return `${base}?token=${encodeURIComponent(token)}`;
};

export const getMerchantByFundKey = (fundKey) => {
  const key = String(fundKey || "")
    .trim()
    .toUpperCase();
  return paymentConfig.parsianConfig.merchants[key];
};

const escapeXml = (value) =>
  String(value ?? "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&apos;");
