import express from "express";
import { chromium, type Browser, type BrowserContext, type Page } from "playwright";

const PORT = Number(process.env.PORT ?? 3003);
const SESSION_TTL_MS = 3 * 60 * 1000; // 3 min

// ── Singleton browser ─────────────────────────────────────────────────────────

let _browser: Browser | null = null;

async function getBrowser(): Promise<Browser> {
  if (!_browser || !_browser.isConnected()) {
    console.log("[gst-service] Launching Chromium…");
    _browser = await chromium.launch({
      headless: true,
      args: ["--no-sandbox", "--disable-setuid-sandbox"],
    });
    console.log("[gst-service] Chromium ready.");
  }
  return _browser;
}

// ── Session store ─────────────────────────────────────────────────────────────

type Session = {
  ctx: BrowserContext;
  page: Page;
  captchaB64: string;
  expires: number;
};

const sessions = new Map<string, Session>();

function makeId() {
  return Math.random().toString(36).slice(2) + Date.now().toString(36);
}

function pruneExpired() {
  const now = Date.now();
  for (const [id, s] of sessions) {
    if (s.expires < now) {
      s.ctx.close().catch(() => {});
      sessions.delete(id);
    }
  }
}

// ── Express app ───────────────────────────────────────────────────────────────

const app = express();
app.use(express.json());

// Allow the Pelagia app to call us (CORS)
app.use((_req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");
  next();
});

app.get("/health", (_req, res) => res.json({ ok: true }));

/**
 * GET /captcha
 * Loads the GST search page, fetches the CAPTCHA image.
 * Returns: { sessionId, captchaBase64 }
 */
app.get("/captcha", async (_req, res) => {
  pruneExpired();
  try {
    const browser = await getBrowser();
    const ctx = await browser.newContext({
      userAgent:
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
      viewport: { width: 1280, height: 900 },
    });
    const page = await ctx.newPage();

    await page.goto("https://services.gst.gov.in/services/searchtp", {
      waitUntil: "networkidle",
      timeout: 30000,
    });

    const captchaB64: string = await page.evaluate(() =>
      fetch("/services/captcha", { headers: { Accept: "image/png,image/*" } })
        .then((r) => r.blob())
        .then(
          (blob) =>
            new Promise<string>((resolve, reject) => {
              const reader = new FileReader();
              reader.onload = () =>
                resolve((reader.result as string).split(",")[1]);
              reader.onerror = reject;
              reader.readAsDataURL(blob);
            })
        )
    );

    if (!captchaB64) throw new Error("Empty CAPTCHA response");

    const sessionId = makeId();
    sessions.set(sessionId, {
      ctx,
      page,
      captchaB64,
      expires: Date.now() + SESSION_TTL_MS,
    });

    console.log(`[gst-service] Session ${sessionId} created`);
    res.json({ sessionId, captchaBase64: captchaB64 });
  } catch (e) {
    console.error("[gst-service] /captcha error:", e);
    res.status(502).json({ error: String(e) });
  }
});

/**
 * POST /search
 * Body: { sessionId, gstin, captcha }
 * Returns taxpayer data or { error }
 */
app.post("/search", async (req, res) => {
  const { sessionId, gstin, captcha } = req.body ?? {};
  if (!sessionId || !gstin || !captcha) {
    return res.status(400).json({ error: "sessionId, gstin and captcha are required" });
  }

  const session = sessions.get(sessionId);
  if (!session || session.expires < Date.now()) {
    sessions.delete(sessionId);
    return res.status(410).json({ error: "Session expired — please fetch a new CAPTCHA." });
  }

  try {
    const raw: Record<string, unknown> = await session.page.evaluate(
      ([g, c]: [string, string]) =>
        fetch("/services/api/search/tp", {
          method: "POST",
          headers: {
            Accept: "application/json, text/plain",
            "Content-Type": "application/json;charset=UTF-8",
          },
          body: JSON.stringify({ gstin: g, captcha: c }),
        })
          .then((r) => r.json())
          .catch((e: Error) => ({ error: e.message })),
      [gstin, captcha] as [string, string]
    );

    // Always close session after use
    session.ctx.close().catch(() => {});
    sessions.delete(sessionId);
    console.log(`[gst-service] Session ${sessionId} closed`);

    if (raw.error) return res.status(502).json({ error: raw.error });
    if (raw.errorCode === "SWEB_9034") return res.status(422).json({ error: "Wrong CAPTCHA — please try again." });
    if (raw.errorCode) return res.status(422).json({ error: `GST portal error: ${raw.errorCode}` });
    if (!raw.gstin) return res.status(422).json({ error: "No data found for that GSTIN." });

    // Parse address / pincode
    const pradr = (raw.pradr as Record<string, unknown>)?.adr as string ?? "";
    const pincodeMatch = pradr.match(/\b(\d{6})\b/);

    res.json({
      legalName:        raw.lgnm ?? "",
      tradeName:        raw.tradeNam ?? raw.lgnm ?? "",
      address:          pradr,
      state:            String(raw.stj ?? "").split(",")[0].replace(/^State\s*-\s*/i, ""),
      pincode:          pincodeMatch?.[1] ?? "",
      gstin:            raw.gstin,
      status:           raw.sts ?? "",
      businessType:     raw.ctb ?? raw.dty ?? "",
      registrationDate: raw.rgdt ?? "",
    });
  } catch (e) {
    console.error("[gst-service] /search error:", e);
    res.status(500).json({ error: String(e) });
  }
});

app.listen(PORT, () => {
  console.log(`[gst-service] Listening on http://localhost:${PORT}`);
});
