/**
 * hwlab-code-agent 客户端核心逻辑
 * 直接调 HWLAB v0.2 cloud-api，自动识别环境变量 HWLAB_API_KEY
 * 遵循 cli-spec：配置从 config.json 单一来源
 */
import { readFileSync } from "node:fs";
import path from "node:path";

// ---- config ----
const SKILL_ROOT = path.resolve(import.meta.dir, "..", "..");
const configPath = path.join(SKILL_ROOT, "config.json");

interface Config {
  apiBaseUrl: string;
  defaultProjectId: string;
  defaultPollTimeoutMs: number;
}

const INHERITED_PROVIDER_PROFILE_ENV = "HWLAB_CODE_AGENT_PROVIDER_PROFILE";
const PARENT_TRACE_ID_ENV = "HWLAB_CODE_AGENT_PARENT_TRACE_ID";

let _cfg: Config | null = null;
function config(): Config {
  if (!_cfg) {
    try {
      _cfg = JSON.parse(readFileSync(configPath, "utf-8")) as Config;
    } catch {
      throw new Error(`hwlab-code-agent: failed to load config from ${configPath}`);
    }
    if (!_cfg.apiBaseUrl) throw new Error("hwlab-code-agent: config.apiBaseUrl is required");
  }
  return _cfg;
}

function textValue(value: string | undefined): string {
  return String(value ?? "").trim();
}

function resolveProviderProfile(args: Record<string, string | undefined>) {
  const explicit = textValue(args.profile || args.providerProfile);
  if (explicit) return { profile: explicit, source: "explicit" };
  const inherited = textValue(process.env[INHERITED_PROVIDER_PROFILE_ENV]);
  if (inherited) return { profile: inherited, source: "env" };
  fail("provider_profile_required", `spawn requires --profile or ${INHERITED_PROVIDER_PROFILE_ENV}`);
}

const REMOVED_WORKSPACE_FILE_ARGS = new Map([
  ["specPath", "--specPath"],
  ["spec-path", "--spec-path"]
]);
const REMOVED_WORKSPACE_FILE_ENV = ["HWPOD_SPEC_CONTENT", "HWPOD_SPEC"];

export function findRemovedWorkspaceFileInput(args: Record<string, string | undefined>, env: Record<string, string | undefined> = process.env): string | null {
  for (const [key, label] of REMOVED_WORKSPACE_FILE_ARGS.entries()) {
    if (textValue(args[key])) return label;
  }
  for (const name of REMOVED_WORKSPACE_FILE_ENV) {
    if (textValue(env[name])) return name;
  }
  return null;
}

export function createAgentChatRequestBody(input: {
  message: string;
  providerProfile: string;
  sessionId: string;
  conversationId: string;
  projectId: string;
  traceId: string;
}): Record<string, unknown> {
  return {
    message: input.message,
    providerProfile: input.providerProfile,
    sessionId: input.sessionId,
    conversationId: input.conversationId,
    projectId: input.projectId,
    traceId: input.traceId,
    shortConnection: true
  };
}

function apiKey(): string {
  const key = process.env.HWLAB_API_KEY;
  if (!key) throw new Error("HWLAB_API_KEY not set in environment");
  return key;
}

function authHeaders(): Record<string, string> {
  return { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey()}` };
}

// ---- JSON out ----
function out(data: Record<string, unknown>, exitCode = 0): void {
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
  if (exitCode !== 0) process.exit(exitCode);
}

function fail(code: string, msg: string, exitCode = 1): never {
  process.stderr.write(JSON.stringify({ ok: false, error: { code, message: msg } }) + "\n");
  process.exit(exitCode);
}

// ---- API ----
export interface SpawnResult {
  ok: boolean;
  action: string;
  sessionId: string;
  traceId: string;
  conversationId: string;
  accepted: boolean;
  acceptedBody: unknown;
}

export async function spawn(args: Record<string, string | undefined>): Promise<void> {
  const cfg = config();
  const resolvedProfile = resolveProviderProfile(args);
  const profile = resolvedProfile.profile;
  const projectId = args.projectId || cfg.defaultProjectId;
  const message = args.message || (args.messageFile ? readFileSync(args.messageFile, "utf-8") : null);
  if (!message) fail("message_required", "--message or --message-file required");
  const removedWorkspaceFileInput = findRemovedWorkspaceFileInput(args);
  if (removedWorkspaceFileInput) {
    fail("legacy_workspace_files_removed", `${removedWorkspaceFileInput} was removed; use AgentRun ResourceBundleRef kind=gitbundle with bundles[]`);
  }

  // session
  let sessionId = args.sessionId || "";
  let conversationId = args.conversationId || "";
  if (!sessionId) {
    const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/sessions`, {
      method: "POST", headers: authHeaders(),
      body: JSON.stringify({ providerProfile: profile, projectId })
    });
    const b = await r.json() as Record<string, unknown>;
    if (!r.ok) fail("session_create_failed", JSON.stringify(b));
    const s = (b.session || b) as Record<string, unknown>;
    sessionId = s.sessionId as string;
    conversationId = conversationId || (s.conversationId as string) || "";
  }
  if (!conversationId) conversationId = `cnv_cli_${Date.now()}`;

  // submit
  const traceId = `trc_${profile}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
  const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat`, {
    method: "POST",
    headers: { ...authHeaders(), "prefer": "respond-async", "x-trace-id": traceId, "x-hwlab-short-connection": "1" },
    body: JSON.stringify(createAgentChatRequestBody({ message, providerProfile: profile, sessionId, conversationId, projectId, traceId }))
  });
  const b = await r.json();
  out({
    ok: true, action: "spawn",
    sessionId, traceId, conversationId,
    providerProfile: profile,
    resolvedProviderProfile: profile,
    profileSource: resolvedProfile.source,
    parentTraceId: textValue(process.env[PARENT_TRACE_ID_ENV]) || null,
    accepted: r.ok,
    acceptedBody: b,
    pollCommand: `bun scripts/hwlab-code-agent-cli.ts poll ${traceId}`,
    resultCommand: `bun scripts/hwlab-code-agent-cli.ts result ${traceId}`
  });
}

export async function poll(traceId: string, args: Record<string, string | undefined>): Promise<void> {
  const cfg = config();
  const timeout = parseInt(args.timeout || String(cfg.defaultPollTimeoutMs), 10);
  const deadline = Date.now() + timeout;

  while (Date.now() < deadline) {
    const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat/result/${encodeURIComponent(traceId)}`, {
      headers: authHeaders()
    });
    const b = await r.json() as Record<string, unknown>;
    const s = (b.status || b.traceStatus) as string;
    if (s === "completed" || s === "result") {
      out({ ok: true, action: "poll", traceId, status: "completed", body: b });
      return;
    }
    if (s === "failed" || s === "error") {
      fail("poll_failed", JSON.stringify(b));
    }
    await new Promise(r => setTimeout(r, 2000));
  }
  fail("poll_timeout", `timeout after ${timeout}ms`);
}

export async function getResult(traceId: string): Promise<void> {
  const cfg = config();
  const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat/result/${encodeURIComponent(traceId)}`, {
    headers: authHeaders()
  });
  out({ ok: r.ok, action: "result", traceId, body: await r.json() });
}

export async function getTrace(traceId: string, args: Record<string, string | undefined>): Promise<void> {
  const cfg = config();
  const q = args.full ? "?full=true" : "";
  const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat/trace/${encodeURIComponent(traceId)}${q}`, {
    headers: authHeaders()
  });
  out({ ok: r.ok, action: "trace", traceId, body: await r.json() });
}
