#!/usr/bin/env node
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { HermesAgent } from "../src/hermes.js";
import { Storage } from "../src/storage.js";
import { leadExportFilename, leadsCsv, leadsJson, portfolioMarkdown, portfolioReportFilename } from "../src/export.js";
import { loadEnv } from "../src/utils.js";

const TERMINAL_STATUSES = new Set(["complete", "partial", "failed", "cancelled", "killed"]);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, "..");

loadEnv(path.join(rootDir, ".env"));

const args = parseArgs(process.argv.slice(2));
if (args.help) usage();

const inputFile = args.input ? await readInputFile(args.input) : {};
const niche = text(args.niche || inputFile.niche);
if (!niche) usage("Missing required --niche value.");

const runInput = {
  source: "multi-source-owner-evidence",
  strategyVersion: "owner-first-v4",
  niche,
  location: text(args.location || args.city || inputFile.location || inputFile.city || "United States"),
  country: text(args.country || inputFile.country || "United States"),
  target: number(args.target || inputFile.target, 5, 1, 1000),
  minScore: number(args.minScore || inputFile.minScore, 60, 1, 100),
  leadWorkerConcurrency: number(args.leadWorkers || inputFile.leadWorkerConcurrency, 3, 1, 20),
  ownerAgentSwarmSize: number(args.ownerAgents || inputFile.ownerAgentSwarmSize, 20, 1, 50),
  maxPlannedLocations: number(args.maxLocations || inputFile.maxPlannedLocations, 20, 1, 500),
  ownerHuntTimeoutMs: number(args.timeoutMs || inputFile.ownerHuntTimeoutMs || process.env.HERMES_OWNER_HUNT_TIMEOUT_MS, 90000, 10000, 600000),
  criteria: text(args.criteria || inputFile.criteria || ownerHunterCriteria())
};

const storage = new Storage(path.join(rootDir, "data"));
await storage.init();
const agent = new HermesAgent({
  projectName: "OwnerHunter",
  rootDir,
  storage
});

const run = await agent.startRun(runInput);
console.log(`Started owner-hunter run: ${run.id}`);
console.log(`Niche: ${runInput.niche}`);
console.log(`Location: ${runInput.location}, ${runInput.country}`);
console.log(`Target: ${runInput.target}`);

process.on("SIGINT", async () => {
  console.log("\nCancelling run...");
  await agent.cancelRun(run.id).catch(() => {});
  process.exit(130);
});

let lastLine = "";
let finalRun = run;
while (true) {
  await sleep(3000);
  finalRun = await storage.getRun(run.id);
  if (!finalRun) throw new Error(`Run disappeared from storage: ${run.id}`);
  const counters = finalRun.counters || {};
  const line = [
    `status=${finalRun.status}`,
    `qualified=${finalRun.qualifiedLeads?.length || 0}/${finalRun.input?.target || runInput.target}`,
    `discovered=${counters.discovered || 0}`,
    `ownerHunts=${counters.ownerRabbitHoles || 0}`,
    `ownerProfiles=${counters.ownerProfilesFound || 0}`,
    `ownerContacts=${counters.ownerContactsFound || 0}`,
    `failures=${finalRun.failures?.length || 0}`
  ].join(" ");
  if (line !== lastLine) {
    console.log(line);
    lastLine = line;
  }
  if (TERMINAL_STATUSES.has(finalRun.status)) break;
}

const exportDir = path.join(rootDir, "exports", run.id);
await mkdir(exportDir, { recursive: true });
const csvName = leadExportFilename(finalRun);
const reportName = portfolioReportFilename(finalRun);
await writeFile(path.join(exportDir, csvName), leadsCsv(finalRun));
await writeFile(path.join(exportDir, "leads.json"), JSON.stringify(leadsJson(finalRun), null, 2));
await writeFile(path.join(exportDir, reportName), portfolioMarkdown(finalRun));
await writeFile(path.join(exportDir, "run.json"), JSON.stringify(finalRun, null, 2));

console.log(`Finished: ${finalRun.status}`);
console.log(`Exports: ${exportDir}`);
console.log(`CSV: ${path.join(exportDir, csvName)}`);
console.log(`Report: ${path.join(exportDir, reportName)}`);
if (finalRun.status === "failed") process.exitCode = 1;

function parseArgs(argv) {
  const parsed = {};
  const positionals = [];
  for (let i = 0; i < argv.length; i += 1) {
    const item = argv[i];
    if (!item.startsWith("--")) {
      positionals.push(item);
      continue;
    }
    const [rawKey, inlineValue] = item.slice(2).split("=", 2);
    const key = rawKey.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
    if (inlineValue !== undefined) parsed[key] = inlineValue;
    else if (argv[i + 1] && !argv[i + 1].startsWith("--")) parsed[key] = argv[++i];
    else parsed[key] = true;
  }
  if (!parsed.niche && positionals[0]) parsed.niche = positionals[0];
  if (!parsed.location && positionals[1]) parsed.location = positionals[1];
  if (!parsed.target && positionals[2]) parsed.target = positionals[2];
  return parsed;
}

async function readInputFile(filePath) {
  const resolved = path.resolve(rootDir, filePath);
  return JSON.parse(await readFile(resolved, "utf8"));
}

function text(value) {
  return String(value || "").trim();
}

function number(value, fallback, min, max) {
  const parsed = Number(value);
  const safe = Number.isFinite(parsed) ? parsed : fallback;
  return Math.max(min, Math.min(max, safe));
}

function ownerHunterCriteria() {
  return [
    "Use Google Maps, business websites, directories, registries, booking platforms, social pages, and local articles only as seed/evidence sources.",
    "Identify a real person owner first, prove that owner with public evidence, search that person's public profile/contact trail, and return the best available owner contact.",
    "Business email or phone is seed evidence only. Final qualification needs owner-level person proof and owner-level email or phone evidence."
  ].join(" ");
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function usage(message = "") {
  if (message) console.error(message);
  console.error(`
Usage:
  npm run owner:run -- --niche "nail salon" --location "Austin, TX" --target 5
  npm run owner:run -- --input examples/owner-hunter-input.empty.json

Options:
  --niche              Required business niche.
  --location           City, state, or United States scope.
  --country            Country scope. Defaults to United States.
  --target             Number of qualified owner leads. Defaults to 5.
  --min-score          Minimum score. Defaults to 60.
  --lead-workers       Business enrichment concurrency, 1-20. Defaults to 3.
  --owner-agents       Owner-search swarm size, 1-50. Defaults to 20.
  --max-locations      Maximum planned locations, 1-500. Defaults to 20.
  --timeout-ms         Per-business owner hunt timeout. Defaults to 90000.
`);
  process.exit(message ? 1 : 0);
}
