import type { UniDeskConfig } from "../config";
import type { RenderedCliResult } from "../output";

import { readCodexPoolConfig } from "./config";
import { boolField, compactCapture, parseJsonOutput, runRemoteCodexPoolScript } from "./remote";
import { codexPoolRuntimeTarget, defaultCodexPoolRuntimeTargetId } from "./runtime-target";
import { codexPoolConfigPath, type CodexErrorPassthroughRule } from "./types";

type Action = "list" | "apply" | "delete";

interface Options {
  action: Action;
  template: string | null;
  confirm: boolean;
  json: boolean;
  targetId: string;
}

export async function codexPoolErrorPassthrough(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
  if (args.includes("--help")) return help();
  const options = parseOptions(args);
  const pool = readCodexPoolConfig();
  const target = codexPoolRuntimeTarget(options.targetId);
  const selected = options.template === null ? null : pool.runtime.errorPassthroughRulesById[options.template];
  if (options.template !== null && selected === undefined) {
    throw new Error(`unknown error-passthrough template ${options.template}; available: ${pool.runtime.errorPassthroughRules.map((item) => item.id).join(", ")}`);
  }
  const payload = {
    action: options.action,
    confirm: options.confirm,
    selected: selected === null || selected === undefined ? null : apiRule(selected),
    managed: pool.runtime.errorPassthroughRules.map((rule) => ({ id: rule.id, ...apiRule(rule) })),
  };
  const result = await runRemoteCodexPoolScript(config, "error-passthrough", remoteScript(payload, target), target);
  const parsed = parseJsonOutput(result.stdout);
  const ok = result.exitCode === 0 && boolField(parsed, "ok", false);
  const response = {
    ok,
    action: `platform-infra-sub2api-codex-pool-error-passthrough-${options.action}`,
    target: { id: target.id, route: target.route, runtimeMode: target.runtimeMode, endpoint: target.serviceDns },
    source: { configPath: `${codexPoolConfigPath}#runtime.errorPassthroughRules`, api: "/api/v1/admin/error-passthrough-rules", official: true },
    request: { template: options.template, confirm: options.confirm },
    result: parsed,
    remote: compactCapture(result, { full: parsed === null || result.exitCode !== 0 }),
    valuesPrinted: false,
  };
  return options.json ? response : render(response);
}

function parseOptions(args: string[]): Options {
  const [actionRaw = "list", ...rest] = args;
  if (actionRaw !== "list" && actionRaw !== "apply" && actionRaw !== "delete") throw new Error("error-passthrough usage: list|apply|delete [--template <id>] [--confirm] [--target <id>] [--json]");
  let template: string | null = null;
  let confirm = false;
  let json = false;
  let targetId = defaultCodexPoolRuntimeTargetId();
  for (let index = 0; index < rest.length; index += 1) {
    const arg = rest[index]!;
    const readValue = (name: string): string => {
      const value = rest[index + 1];
      if (!value || value.startsWith("--")) throw new Error(`${name} requires a value`);
      index += 1;
      return value;
    };
    if (arg === "--template") template = readValue("--template");
    else if (arg.startsWith("--template=")) template = arg.slice("--template=".length);
    else if (arg === "--target") targetId = readValue("--target");
    else if (arg.startsWith("--target=")) targetId = arg.slice("--target=".length);
    else if (arg === "--confirm") confirm = true;
    else if (arg === "--json") json = true;
    else throw new Error(`unsupported error-passthrough option: ${arg}`);
  }
  if ((actionRaw === "apply" || actionRaw === "delete") && template === null) throw new Error(`error-passthrough ${actionRaw} requires --template <id>`);
  if (actionRaw === "list" && (template !== null || confirm)) throw new Error("error-passthrough list does not accept --template or --confirm");
  return { action: actionRaw, template, confirm, json, targetId };
}

function apiRule(rule: CodexErrorPassthroughRule): Record<string, unknown> {
  return {
    name: rule.name,
    enabled: rule.enabled,
    priority: rule.priority,
    error_codes: rule.errorCodes,
    keywords: rule.keywords,
    match_mode: rule.matchMode,
    platforms: rule.platforms,
    passthrough_code: rule.passthroughCode,
    response_code: rule.responseCode,
    passthrough_body: rule.passthroughBody,
    custom_message: rule.customMessage,
    skip_monitoring: rule.skipMonitoring,
    description: rule.description,
  };
}

function help(): RenderedCliResult {
  return {
    ok: true,
    command: "platform-infra sub2api codex-pool error-passthrough --help",
    renderedText: [
      "SUB2API ERROR PASSTHROUGH",
      "Usage:",
      "  bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough list [--target PK01] [--json]",
      "  bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough apply --template <id> [--target PK01] [--confirm] [--json]",
      "  bun scripts/cli.ts platform-infra sub2api codex-pool error-passthrough delete --template <id> [--target PK01] [--confirm] [--json]",
      "Writes use the official Sub2API admin error-passthrough-rules API. Without --confirm, apply/delete are dry-run.",
    ].join("\n"),
    contentType: "text/plain",
  };
}

function render(response: Record<string, unknown>): RenderedCliResult {
  const result = record(response.result);
  const rows = Array.isArray(result.rules) ? result.rules.map(record) : [];
  const lines = [
    `SUB2API ERROR PASSTHROUGH ok=${response.ok === true ? "true" : "false"} mode=${text(result.mode)} mutation=${text(result.mutation)}`,
    "TEMPLATE                             RULE_ID  ENABLED  MATCH  ERROR_CODES  RESPONSE  PLATFORMS  STATE",
  ];
  for (const row of rows) {
    lines.push([
      text(row.templateId).padEnd(36),
      text(row.ruleId).padEnd(7),
      text(row.enabled).padEnd(7),
      text(row.matchMode).padEnd(5),
      text(row.errorCodes).padEnd(11),
      text(row.responseCode).padEnd(8),
      text(row.platforms).padEnd(9),
      text(row.state),
    ].join("  ").trimEnd());
  }
  if (result.unmanagedCount !== undefined) lines.push(`UNMANAGED ${text(result.unmanagedCount)}`);
  if (result.message) lines.push(`MESSAGE ${text(result.message)}`);
  return { ok: response.ok === true, command: String(response.action), renderedText: lines.join("\n"), contentType: "text/plain", projection: response };
}

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

function text(value: unknown): string {
  if (value === null || value === undefined || value === "") return "-";
  if (Array.isArray(value)) return value.join(",");
  if (typeof value === "boolean") return value ? "yes" : "no";
  return String(value).replace(/[\r\n\t]+/gu, " ");
}

function remoteScript(payload: unknown, target: ReturnType<typeof codexPoolRuntimeTarget>): string {
  const encoded = Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
  return `set -u
python3 - <<'PY'
import base64, json, subprocess
from urllib.parse import urlencode

PAYLOAD = json.loads(base64.b64decode(${JSON.stringify(encoded)}).decode("utf-8"))
RUNTIME_MODE = ${JSON.stringify(target.runtimeMode)}
NAMESPACE = ${JSON.stringify(target.namespace)}
APP_SECRET_NAME = ${JSON.stringify(target.appSecretName)}
HOST_PORT = ${JSON.stringify(target.hostDockerAppPort)}
APP_POD = None

def run(cmd, body=None):
    return subprocess.run(cmd, input=body, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def docker(args):
    proc = run(["docker", *args])
    return proc if proc.returncode == 0 else run(["sudo", "-n", "docker", *args])

def kubectl(args, body=None):
    return run(["kubectl", *args], body)

def kube_json(args):
    proc = kubectl([*args, "-o", "json"])
    if proc.returncode != 0: raise RuntimeError("kubectl read failed")
    return json.loads(proc.stdout.decode())

if RUNTIME_MODE != "host-docker":
    pods = kube_json(["-n", NAMESPACE, "get", "pods", "-l", "app=sub2api"]).get("items") or []
    running = [x for x in pods if x.get("status", {}).get("phase") == "Running"]
    if not running: raise RuntimeError("sub2api app pod not found")
    APP_POD = running[0]["metadata"]["name"]

def env_value(key, default=None):
    if RUNTIME_MODE == "host-docker":
        proc = docker(["inspect", "sub2api-app", "--format", "{{json .Config.Env}}"])
        if proc.returncode != 0: return default
        for item in json.loads(proc.stdout.decode()):
            if item.startswith(key + "="): return item.split("=", 1)[1]
        return default
    cm = kube_json(["-n", NAMESPACE, "get", "configmap", "sub2api-config"]).get("data") or {}
    return cm.get(key, default)

def secret_value(key):
    if RUNTIME_MODE == "host-docker": return env_value(key)
    data = kube_json(["-n", NAMESPACE, "get", "secret", APP_SECRET_NAME]).get("data") or {}
    return base64.b64decode(data[key]).decode() if key in data else None

def api(method, path, token=None, payload=None):
    body = b"" if payload is None else json.dumps(payload, separators=(",", ":")).encode()
    script = '''set -eu
method="$1"; url="$2"; token="$3"; tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT; cat >"$tmp"
args=""; [ -n "$token" ] && args="Authorization: Bearer $token"
if [ -n "$args" ] && [ -s "$tmp" ]; then curl -sS -w '\n__CODE__:%{http_code}' -X "$method" -H "$args" -H 'Content-Type: application/json' --data-binary @"$tmp" "$url"
elif [ -n "$args" ]; then curl -sS -w '\n__CODE__:%{http_code}' -X "$method" -H "$args" "$url"
elif [ -s "$tmp" ]; then curl -sS -w '\n__CODE__:%{http_code}' -X "$method" -H 'Content-Type: application/json' --data-binary @"$tmp" "$url"
else curl -sS -w '\n__CODE__:%{http_code}' -X "$method" "$url"; fi'''
    base = f"http://127.0.0.1:{HOST_PORT}" if RUNTIME_MODE == "host-docker" else "http://127.0.0.1:8080"
    cmd = ["sh", "-c", script, "sh", method, base + path, token or ""]
    proc = run(cmd, body) if RUNTIME_MODE == "host-docker" else kubectl(["-n", NAMESPACE, "exec", "-i", APP_POD, "--", *cmd], body)
    out = proc.stdout.decode(errors="replace"); marker = "\\n__CODE__:"; pos = out.rfind(marker)
    code = int(out[pos + len(marker):].strip()) if pos >= 0 else 0; raw = out[:pos] if pos >= 0 else out
    parsed = json.loads(raw) if raw.strip() else None
    if proc.returncode != 0 or not 200 <= code < 300 or (isinstance(parsed, dict) and parsed.get("code") not in (None, 0)):
        raise RuntimeError(f"official admin API failed method={method} path={path} http={code}")
    return parsed.get("data") if isinstance(parsed, dict) and "data" in parsed else parsed

def login():
    password = secret_value("ADMIN_PASSWORD")
    if not password: raise RuntimeError("ADMIN_PASSWORD missing")
    data = api("POST", "/api/v1/auth/login", payload={"email": env_value("ADMIN_EMAIL", "admin@example.com"), "password": password})
    token = data.get("access_token") or data.get("token") if isinstance(data, dict) else None
    if not token: raise RuntimeError("admin login response has no token")
    return token

FIELDS = ("name","enabled","priority","error_codes","keywords","match_mode","platforms","passthrough_code","response_code","passthrough_body","custom_message","skip_monitoring","description")
def normalized(rule): return {k: rule.get(k) for k in FIELDS}
def state(current, desired):
    if current is None: return "missing"
    return "synced" if normalized(current) == normalized(desired) else "drift"

def execute():
    token = login(); current = api("GET", "/api/v1/admin/error-passthrough-rules", token=token) or []
    if not isinstance(current, list): raise RuntimeError("official rule list is not an array")
    managed = PAYLOAD.get("managed") or []; selected = PAYLOAD.get("selected")
    action = PAYLOAD["action"]; confirm = bool(PAYLOAD.get("confirm")); mutation = False
    if action in ("apply", "delete"):
        matches = [r for r in current if r.get("name") == selected.get("name")]
        if len(matches) > 1: raise RuntimeError("duplicate official rules with managed name")
        existing = matches[0] if matches else None
        if action == "apply" and state(existing, selected) != "synced" and confirm:
            if existing is None: api("POST", "/api/v1/admin/error-passthrough-rules", token, selected)
            else: api("PUT", f"/api/v1/admin/error-passthrough-rules/{existing['id']}", token, selected)
            mutation = True
        if action == "delete" and existing is not None and confirm:
            api("DELETE", f"/api/v1/admin/error-passthrough-rules/{existing['id']}", token)
            mutation = True
        current = api("GET", "/api/v1/admin/error-passthrough-rules", token=token) or []
    rows = []
    names = {m["name"] for m in managed}
    for desired in managed:
        found = [r for r in current if r.get("name") == desired["name"]]
        actual = found[0] if len(found) == 1 else None
        row_state = "duplicate" if len(found) > 1 else state(actual, desired)
        if action == "delete" and selected and desired["name"] == selected["name"] and actual is None: row_state = "absent"
        rows.append({"templateId": desired["id"], "ruleId": actual.get("id") if actual else None, "enabled": actual.get("enabled") if actual else desired.get("enabled"), "matchMode": desired.get("match_mode"), "errorCodes": desired.get("error_codes"), "responseCode": desired.get("response_code"), "platforms": desired.get("platforms"), "state": row_state})
    target_row = next((x for x in rows if selected and x["templateId"] == next((m["id"] for m in managed if m["name"] == selected["name"]), None)), None)
    ok = all(x["state"] not in ("duplicate",) for x in rows)
    if action == "apply" and confirm: ok = ok and target_row is not None and target_row["state"] == "synced"
    if action == "delete" and confirm: ok = ok and target_row is not None and target_row["state"] == "absent"
    return {"ok": ok, "mode": "confirm" if confirm else "dry-run" if action != "list" else "list", "mutation": mutation, "rules": rows, "unmanagedCount": sum(1 for r in current if r.get("name") not in names), "message": "official Sub2API error-passthrough-rules API"}

try: output = execute()
except Exception as exc: output = {"ok": False, "error": str(exc), "mutation": False, "rules": []}
print(json.dumps(output, ensure_ascii=False, indent=2))
raise SystemExit(0 if output.get("ok") else 1)
PY`;
}
