import { existsSync } from "node:fs";
import { readFile } from "node:fs/promises";
import path from "node:path";
import { resolveMapsPython } from "../pythonRuntime.js";
import { processSupervisor } from "../processSupervisor.js";
import { csvParse, safeFilePart } from "../utils.js";

export class GoogleMapsScrapperAdapter {
  constructor({ rootDir, storage }) {
    this.rootDir = rootDir;
    this.storage = storage;
    this.python = resolveMapsPython(rootDir);
    this.scriptPath = path.resolve(rootDir, process.env.GOOGLE_MAPS_SCRAPPER_PATH || "tools/vendor/Google-Maps-Scrapper/main.py");
  }

  available() {
    return existsSync(this.scriptPath);
  }

  async discover({ runId, query, total, attempt }) {
    if (!this.available()) {
      throw new Error(`Google-Maps-Scrapper missing at ${this.scriptPath}. Run scripts/bootstrap_tools.sh first.`);
    }

    const output = path.join(this.storage.rawDir, `${runId}-${attempt}-${safeFilePart(query)}.csv`);
    const args = [this.scriptPath, "-s", query, "-t", String(total), "-o", output];
    const result = await runProcess(this.python, args, {
      cwd: path.dirname(this.scriptPath),
      timeoutMs: Number(process.env.HERMES_FETCH_TIMEOUT_MS || 120000)
    });

    if (result.code !== 0) {
      throw new Error(`Google-Maps-Scrapper failed: ${result.stderr || result.stdout}`);
    }

    const csv = await readFile(output, "utf8");
    return {
      rawPath: output,
      rows: csvParse(csv),
      stdout: result.stdout
    };
  }
}

function runProcess(command, args, { cwd, timeoutMs }) {
  return new Promise((resolve) => {
    const detached = process.platform !== "win32";
    const child = processSupervisor.spawn(command, args, {
      cwd,
      stdio: ["ignore", "pipe", "pipe"],
      detached
    }, { label: "google-maps-scrapper", killGroup: true });
    let stdout = "";
    let stderr = "";
    const timer = setTimeout(() => {
      killProcess(child, "SIGTERM");
      stderr += `\nTimed out after ${timeoutMs}ms`;
      setTimeout(() => killProcess(child, "SIGKILL"), 5000).unref?.();
    }, timeoutMs);

    child.stdout.on("data", (chunk) => { stdout += chunk.toString(); });
    child.stderr.on("data", (chunk) => { stderr += chunk.toString(); });
    child.on("close", (code) => {
      clearTimeout(timer);
      resolve({ code, stdout, stderr });
    });
    child.on("error", (error) => {
      clearTimeout(timer);
      resolve({ code: 1, stdout, stderr: `${stderr}\n${error.message}` });
    });
  });
}

function killProcess(child, signal) {
  processSupervisor.killChild(child, signal);
}
