#!/usr/bin/env node

import { Command } from "commander";
import { loadConfig } from "./config.js";
import { runDoctor } from "./doctor.js";
import { launchFromFile } from "./launch.js";
import { scanOnce } from "./scan.js";
import { claimFeesOnce } from "./claim.js";
import { executeBuyback } from "./buyback.js";
import { runAgent } from "./run.js";
import { AgentState } from "./state.js";
import { PONS_WETH } from "./contracts.js";
import { formatEther } from "viem";
import { reconcileExecutions } from "./reconcile.js";
import { EXECUTION_LEASE_NAME, ExecutionLease } from "./lease.js";
import { resolveExecutionConfig } from "./wallet-vault.js";

const withManualExecutionLease = async <T>(work: (config: ReturnType<typeof loadConfig>) => Promise<T>) => {
  const config = resolveExecutionConfig(loadConfig());
  if (config.mode !== "live") return work(config);
  const lease = new ExecutionLease(config.stateDbPath);
  if (!lease.acquire()) throw new Error("The Agent runner or another manual command owns the wallet execution lease");
  try {
    const result = await work(config);
    lease.assertActive();
    return result;
  } finally {
    lease.release();
  }
};

const program = new Command();

program
  .name("zoi-agent")
  .description("Self-hosted trend, launch, fee claim, and buyback agent for Pons")
  .version("0.1.0");

program.command("doctor")
  .description("Verify chain, Pons, wallet, and provider configuration")
  .action(async () => {
    const checks = await runDoctor(loadConfig());
    for (const check of checks) {
      const marker = check.status === "pass" ? "PASS" : check.status === "warn" ? "WARN" : "FAIL";
      process.stdout.write(`${marker.padEnd(4)}  ${check.name.padEnd(20)} ${check.detail}\n`);
    }
    if (checks.some((check) => check.status === "fail")) process.exitCode = 1;
  });

program.command("launch")
  .description("Simulate or execute a Pons launch from a JSON specification")
  .requiredOption("--file <path>", "Token launch JSON file")
  .option("--initial-buy <eth>", "Creator initial buy in ETH", "0")
  .action(async (options: { file: string; initialBuy: string }) => {
    const result = await withManualExecutionLease((config) => launchFromFile(config, options.file, options.initialBuy));
    process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
  });

program.command("scan-once")
  .description("Fetch current X trends and produce validated 5.6sol decisions without trading")
  .action(async () => {
    const result = await scanOnce(loadConfig());
    process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
  });

program.command("claim-once")
  .description("Inspect or claim creator fees for all Agent-launched tokens")
  .action(async () => {
    const result = await withManualExecutionLease((config) => claimFeesOnce(config));
    process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
  });

program.command("buyback")
  .description("Simulate or execute a WETH buyback against a verified Pons token")
  .requiredOption("--token <address>", "Pons token contract")
  .requiredOption("--amount <weth>", "Allocated WETH amount")
  .action(async (options: { token: string; amount: string }) => {
    const result = await withManualExecutionLease((config) => executeBuyback(config, options.token, options.amount));
    process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
  });

program.command("status")
  .description("Show local Agent state and allocated buyback balance")
  .action(() => {
    const config = resolveExecutionConfig(loadConfig());
    const walletAddress = config.account?.address;
    const state = new AgentState(config.stateDbPath);
    const status = {
      mode: config.mode,
      wallet: config.account?.address ?? null,
      launches: walletAddress ? state.listLaunches(walletAddress) : [],
      buybackWeth: formatEther(walletAddress ? state.getBucketBalance(walletAddress, "buyback", PONS_WETH) : 0n),
      buybackBalances: walletAddress ? state.listBucketBalances(walletAddress, "buyback").map((entry) => ({ asset: entry.asset, amount: entry.balance.toString() })) : [],
      eventSequence: state.getSequence(),
      publicEventOutbox: state.getPublicEventOutboxStatus(),
      runtimeLock: state.getRuntimeLock(EXECUTION_LEASE_NAME) ?? null,
      recentCycles: state.listRecentCycles(5),
      recentExecutions: walletAddress ? state.listRecentExecutions(walletAddress, 10) : [],
    };
    state.close();
    process.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
  });

program.command("reconcile")
  .description("Reconcile submitted or uncertain executions against Robinhood Chain receipts")
  .action(async () => {
    const result = await reconcileExecutions(resolveExecutionConfig(loadConfig()));
    process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
  });

program.command("run")
  .description("Run the recurring trend and fee-claim worker")
  .option("--once", "Run one complete cycle and exit", false)
  .action(async (options: { once: boolean }) => runAgent(loadConfig(), options.once));

await program.parseAsync();
