import { readFileSync } from "node:fs";
import type { UniDeskConfig } from "../config";
import type { RenderedCliResult } from "../output";
import { resolveAgentRunLaneTarget } from "../agentrun-lanes";
import { runSshCommandCapture } from "../ssh";
import { readAgentRunClientConfig } from "./config";
import { renderMachine, renderedCliResult } from "./render";
import type { AgentRunResourceOptions } from "./utils";

export async function runAgentRunResourceThroughTarget(
  config: UniDeskConfig | null,
  command: string,
  canonicalArgs: string[],
  options: AgentRunResourceOptions,
): Promise<RenderedCliResult | null> {
  const client = readAgentRunClientConfig();
  if (client.client.transport !== "target-trans") return null;
  if (config === null) return null;
  const execution = client.client.targetExecution;
  if (execution === null) throw new Error(`${client.sourcePath}: client.targetExecution is required for target-trans`);
  const { configPath, spec } = resolveAgentRunLaneTarget({ node: options.node, lane: options.lane });
  const activeTarget = process.env[execution.markerEnv] ?? null;
  if (activeTarget === spec.nodeId) return null;
  if (activeTarget !== null) {
    throw new Error(`${execution.markerEnv}=${activeTarget} cannot re-enter a second Target ${spec.nodeId}`);
  }
  const route = `${spec.nodeRoute}:${spec.nodeUnideskWorkspace}`;
  const targetArgs = [...canonicalArgs];
  if (!hasOption(targetArgs, "target")) targetArgs.push("--target", spec.nodeId);
  if (!hasOption(targetArgs, "lane")) targetArgs.push("--lane", spec.lane);
  const stdin = targetExecutionStdin(targetArgs);
  const capture = await runSshCommandCapture(config, route, [
    "argv",
    "env",
    `${execution.markerEnv}=${spec.nodeId}`,
    execution.executable,
    execution.cliPath,
    "agentrun",
    ...targetArgs,
  ], stdin);
  const disclosure = {
    transport: "target-trans",
    target: spec.nodeId,
    lane: spec.lane,
    route,
    configPath,
    unideskWorkspace: spec.nodeUnideskWorkspace,
    markerEnv: execution.markerEnv,
    mutation: false,
    valuesPrinted: false,
  };
  if (capture.exitCode !== 0) {
    const detail = boundedRemoteFailure(capture.stderr || capture.stdout);
    return renderedCliResult(
      false,
      command,
      [
        `ERROR  target-trans/${spec.nodeId}`,
        `route: ${route}`,
        `exitCode: ${capture.exitCode}`,
        `message: ${detail || "Target CLI did not return output"}`,
      ].join("\n"),
    );
  }

  const output = capture.stdout.trimEnd();
  if (options.raw || options.output === "json") {
    const payload = parseTargetMachineOutput(output, "json");
    return renderMachine(command, attachTargetExecution(payload, disclosure), "json", machineOutputOk(payload));
  }
  if (options.output === "yaml") {
    const payload = parseTargetMachineOutput(output, "yaml");
    return renderMachine(command, attachTargetExecution(payload, disclosure), "yaml", machineOutputOk(payload));
  }
  return renderedCliResult(
    true,
    command,
    [`TargetExecution: trans ${route} lane=${spec.lane}`, output].filter(Boolean).join("\n"),
  );
}

function hasOption(args: string[], name: string): boolean {
  const flag = `--${name}`;
  return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
}

function targetExecutionStdin(args: string[]): string | undefined {
  const stdinFlag = args.some((arg) => arg === "--prompt-stdin" || arg === "--stdin"
    || arg === "--json-stdin" || arg === "--yaml-stdin");
  const stdinFile = optionIsStdin(args, "prompt-file") || optionIsStdin(args, "json-file")
    || optionIsStdin(args, "yaml-file") || optionIsStdin(args, "file") || optionIsStdin(args, "filename")
    || args.some((arg, index) => arg === "-f" && args[index + 1] === "-");
  return stdinFlag || stdinFile ? readFileSync(0, "utf8") : undefined;
}

function optionIsStdin(args: string[], name: string): boolean {
  const flag = `--${name}`;
  return args.some((arg, index) => (arg === flag && args[index + 1] === "-") || arg === `${flag}=-`);
}

function parseTargetMachineOutput(output: string, mode: "json" | "yaml"): unknown {
  if (output.length === 0) throw new Error(`Target CLI returned empty ${mode} output`);
  try {
    return mode === "json" ? JSON.parse(output) : Bun.YAML.parse(output);
  } catch (error) {
    throw new Error(`Target CLI returned invalid ${mode} output: ${error instanceof Error ? error.message : String(error)}`);
  }
}

function attachTargetExecution(value: unknown, disclosure: Record<string, unknown>): Record<string, unknown> {
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
    return { ...(value as Record<string, unknown>), targetExecution: disclosure };
  }
  return { data: value, targetExecution: disclosure };
}

function machineOutputOk(value: unknown): boolean {
  return !(typeof value === "object" && value !== null && (value as Record<string, unknown>).ok === false);
}

function boundedRemoteFailure(value: string): string {
  return value
    .replace(/https?:\/\/[^@\s]+@/gu, "https://[redacted]@")
    .replace(/(token|password|authorization)[=:][^\s]+/giu, "$1=[redacted]")
    .replace(/\s+/gu, " ")
    .trim()
    .slice(0, 600);
}
