import { existsSync, readFileSync } from "node:fs";
import { isAbsolute, join, relative } from "node:path";
import { rootPath } from "./config";
import { CliInputError } from "./output";
import { parseEnvFile, readYamlRecord } from "./platform-infra-ops-library";

interface TaskTreeRuntimeConfig {
  defaults?: {
    node?: unknown;
    backup?: unknown;
  };
  targets?: unknown;
  native?: {
    targetRef?: unknown;
  };
}

interface NodeLanesConfig {
  lanes?: unknown;
}

interface SecretsDistributionConfig {
  sources?: {
    root?: unknown;
  };
}

export function runTaskTreeCommand(args: string[]): number {
  const runtime = readYamlRecord<TaskTreeRuntimeConfig>(
    rootPath("config", "hwlab-tasktree.yaml"),
    "hwlab-tasktree-runtime",
  );
  const targetName = nativeTargetName(runtime.native?.targetRef);
  const target = recordField(recordField(runtime, "targets"), targetName);
  const laneName = stringField(target, "lane", `targets.${targetName}.lane`);
  const nodeName = stringValue(runtime.defaults?.node, "defaults.node");
  const workspace = taskTreeWorkspace(laneName, nodeName);
  const sourceRef = stringField(
    recordField(target, "database", `targets.${targetName}.database`),
    "sourceRef",
    `targets.${targetName}.database.sourceRef`,
  );
  const secretRoot = taskTreeSecretRoot();
  const secretPath = containedPath(secretRoot, sourceRef, "database.sourceRef");
  if (!existsSync(secretPath)) {
    throw new CliInputError("TaskTree 数据库 Secret source 不存在", {
      code: "tasktree_database_secret_missing",
      argument: sourceRef,
      hint: "使用 bun scripts/cli.ts secrets sync/status 核对 YAML 声明的 Secret source。",
    });
  }
  const databaseUrl = parseEnvFile(readFileSync(secretPath, "utf8")).DATABASE_URL;
  if (!databaseUrl) {
    throw new CliInputError("TaskTree 数据库 Secret source 缺少 DATABASE_URL", {
      code: "tasktree_database_url_missing",
      argument: sourceRef,
    });
  }

  const cliPath = join(workspace, "tools", "hwlab-cli", "bin", "hwlab-cli.ts");
  if (!existsSync(cliPath)) {
    throw new CliInputError("YAML 选中的 HWLAB workspace 缺少 TaskTree CLI", {
      code: "tasktree_cli_missing",
      argument: cliPath,
    });
  }

  const childEnv: Record<string, string | undefined> = {
    ...process.env,
    TASKTREE_DATABASE_URL: databaseUrl,
  };
  const overapiEndpoint = optionalNestedString(target, ["runtime", "cli", "overapiEndpoint"]);
  if (overapiEndpoint !== undefined) childEnv.HWLAB_TASKTREE_API_URL = overapiEndpoint;
  if (args[0] === "backup") Object.assign(childEnv, taskTreeBackupEnv(runtime, target));

  const result = Bun.spawnSync({
    cmd: ["bun", cliPath, "tasktree", ...args],
    cwd: workspace,
    env: childEnv,
    stdin: "inherit",
    stdout: "inherit",
    stderr: "inherit",
  });
  return result.exitCode;
}

function nativeTargetName(value: unknown): string {
  const targetRef = stringValue(value, "native.targetRef");
  const match = /^targets\.([A-Za-z0-9_-]+)$/u.exec(targetRef);
  if (match === null) {
    throw new CliInputError("TaskTree native.targetRef 必须指向 targets.<name>", {
      code: "tasktree_invalid_target_ref",
      argument: targetRef,
    });
  }
  return match[1]!;
}

function taskTreeWorkspace(laneName: string, nodeName: string): string {
  const config = readYamlRecord<NodeLanesConfig>(
    rootPath("config", "hwlab-node-lanes.yaml"),
    "HwlabNodeLaneConfig",
  );
  const lane = recordField(recordField(config, "lanes"), laneName, `lanes.${laneName}`);
  const targets = recordField(lane, "targets", `lanes.${laneName}.targets`);
  const target = recordField(targets, nodeName, `lanes.${laneName}.targets.${nodeName}`);
  const workspace = stringField(target, "workspace", `lanes.${laneName}.targets.${nodeName}.workspace`);
  if (!isAbsolute(workspace)) {
    throw new CliInputError("HWLAB workspace 必须是绝对路径", {
      code: "tasktree_invalid_workspace",
      argument: workspace,
    });
  }
  return workspace;
}

function taskTreeSecretRoot(): string {
  const config = readYamlRecord<SecretsDistributionConfig>(
    rootPath("config", "secrets-distribution.yaml"),
    "unidesk-secret-distribution",
  );
  const secretRoot = stringValue(config.sources?.root, "sources.root");
  if (!isAbsolute(secretRoot)) {
    throw new CliInputError("统一 Secret root 必须是绝对路径", {
      code: "tasktree_invalid_secret_root",
      argument: secretRoot,
    });
  }
  return secretRoot;
}

function taskTreeBackupEnv(
  runtime: TaskTreeRuntimeConfig,
  target: Record<string, unknown>,
): Record<string, string> {
  const backup = optionalRecord(runtime.defaults?.backup);
  const targetBackup = optionalRecord(target.backup);
  if (backup === undefined || targetBackup === undefined) {
    throw new CliInputError("TaskTree owning YAML 尚未声明 native 备份配置", {
      code: "tasktree_backup_config_missing",
      hint: "先将 defaults.backup 与当前 target.backup 合入 config/hwlab-tasktree.yaml。",
    });
  }
  return {
    TASKTREE_BACKUP_ENABLED: String(booleanField(backup, "enabled", "defaults.backup.enabled")),
    TASKTREE_BACKUP_REPO_SSH_URL: stringField(backup, "sshUrl", "defaults.backup.sshUrl"),
    TASKTREE_BACKUP_BRANCH: stringField(backup, "branch", "defaults.backup.branch"),
    TASKTREE_BACKUP_BASE_PATH: stringField(targetBackup, "basePath", "target.backup.basePath"),
    TASKTREE_BACKUP_WORKTREE: stringField(backup, "worktreePath", "defaults.backup.worktreePath"),
    TASKTREE_BACKUP_TARGET: stringField(targetBackup, "target", "target.backup.target"),
    TASKTREE_BACKUP_INTERVAL_MS: String(durationMilliseconds(stringField(backup, "interval", "defaults.backup.interval"))),
    TASKTREE_BACKUP_GIT_AUTHOR_NAME: stringField(recordField(backup, "author", "defaults.backup.author"), "name", "defaults.backup.author.name"),
    TASKTREE_BACKUP_GIT_AUTHOR_EMAIL: stringField(recordField(backup, "author", "defaults.backup.author"), "email", "defaults.backup.author.email"),
    TASKTREE_BACKUP_GIT_COMMIT_PREFIX: stringField(backup, "commitMessagePrefix", "defaults.backup.commitMessagePrefix"),
  };
}

function containedPath(root: string, sourceRef: string, label: string): string {
  if (isAbsolute(sourceRef)) {
    throw new CliInputError(`${label} 必须相对统一 Secret root`, {
      code: "tasktree_invalid_secret_source_ref",
      argument: sourceRef,
    });
  }
  const path = join(root, sourceRef);
  const rel = relative(root, path);
  if (rel.startsWith("..") || isAbsolute(rel)) {
    throw new CliInputError(`${label} 超出统一 Secret root`, {
      code: "tasktree_invalid_secret_source_ref",
      argument: sourceRef,
    });
  }
  return path;
}

function durationMilliseconds(value: string): number {
  const match = /^(\d+)(ms|s|m|h)$/u.exec(value);
  if (match === null) {
    throw new CliInputError("TaskTree backup interval 格式无效", {
      code: "tasktree_invalid_backup_interval",
      argument: value,
    });
  }
  const multiplier = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000 }[match[2]!]!;
  return Number(match[1]) * multiplier;
}

function optionalNestedString(record: Record<string, unknown>, path: string[]): string | undefined {
  let value: unknown = record;
  for (const key of path) {
    const current = optionalRecord(value);
    if (current === undefined) return undefined;
    value = current[key];
  }
  return typeof value === "string" && value.length > 0 ? value : undefined;
}

function optionalRecord(value: unknown): Record<string, unknown> | undefined {
  return typeof value === "object" && value !== null && !Array.isArray(value)
    ? value as Record<string, unknown>
    : undefined;
}

function recordField(value: unknown, key: string, label = key): Record<string, unknown> {
  const record = optionalRecord(value);
  const nested = record === undefined ? undefined : optionalRecord(record[key]);
  if (nested === undefined) throw configError(label);
  return nested;
}

function stringField(record: Record<string, unknown>, key: string, label: string): string {
  return stringValue(record[key], label);
}

function stringValue(value: unknown, label: string): string {
  if (typeof value !== "string" || value.length === 0) throw configError(label);
  return value;
}

function booleanField(record: Record<string, unknown>, key: string, label: string): boolean {
  if (typeof record[key] !== "boolean") throw configError(label);
  return record[key];
}

function configError(label: string): CliInputError {
  return new CliInputError(`TaskTree owning YAML 缺少 ${label}`, {
    code: "tasktree_config_invalid",
    argument: label,
  });
}
