import { randomUUID } from "node:crypto";
import { DEFAULT_SERVER, DEFAULT_TIMEOUT } from "./constants.js";
import { CliError } from "./errors.js";
import { requireValue, parseIntStrict } from "./options.js";
import type { AuthContext, GlobalArgs, ParsedGlobalArgs, RuntimeContext } from "./types.js";

function pick(source: Record<string, unknown>, ...keys: string[]): string | null {
  for (const key of keys) {
    const value = source[key];
    if (value !== undefined && value !== null && value !== "") return String(value);
  }
  return null;
}

function readStdin(): Promise<string> {
  return new Promise((resolve, reject) => {
    let body = "";
    process.stdin.setEncoding("utf8");
    process.stdin.on("data", (chunk: string) => {
      body += chunk;
    });
    process.stdin.on("end", () => resolve(body));
    process.stdin.on("error", reject);
  });
}

export async function loadAuthContext(authStdin: boolean): Promise<AuthContext> {
  const raw = authStdin
    ? (JSON.parse(await readStdin()) as Record<string, unknown>)
    : {
        server: process.env.TJWATER_SERVER,
        access_token: process.env.TJWATER_ACCESS_TOKEN,
        project_id: process.env.TJWATER_PROJECT_ID,
        headers: process.env.TJWATER_EXTRA_HEADERS ? JSON.parse(process.env.TJWATER_EXTRA_HEADERS) : {},
      };
  const headers = (raw.headers ?? {}) as unknown;
  if (!headers || Array.isArray(headers) || typeof headers !== "object") {
    throw new CliError("认证失败", "AUTH_CONTEXT_INVALID", "auth context headers must be a JSON object", 3);
  }
  return {
    server: pick(raw, "server", "base_url"),
    accessToken: pick(raw, "access_token", "token", "accessToken"),
    projectId: pick(raw, "project_id", "projectId", "x_project_id"),
    headers: Object.fromEntries(Object.entries(headers as Record<string, unknown>).map(([key, value]) => [String(key), String(value)])),
  };
}

export function parseGlobalArgs(argv: string[]): ParsedGlobalArgs {
  const globals: GlobalArgs = {
    server: null,
    authStdin: false,
    scheme: null,
    timeout: DEFAULT_TIMEOUT,
    requestId: null,
  };
  const rest: string[] = [];
  for (let i = 0; i < argv.length; i += 1) {
    const arg = argv[i]!;
    if (arg === "--auth-stdin") globals.authStdin = true;
    else if (arg === "--server") globals.server = requireValue(argv, ++i, "--server");
    else if (arg === "--scheme") globals.scheme = requireValue(argv, ++i, "--scheme");
    else if (arg === "--timeout") globals.timeout = parseIntStrict(requireValue(argv, ++i, "--timeout"), "--timeout");
    else if (arg === "--request-id") globals.requestId = requireValue(argv, ++i, "--request-id");
    else rest.push(arg);
  }
  return { globals, rest };
}

export async function buildRuntime(globals: GlobalArgs): Promise<RuntimeContext> {
  const auth = await loadAuthContext(globals.authStdin);
  return {
    server: globals.server || auth.server || DEFAULT_SERVER,
    auth,
    scheme: globals.scheme,
    timeout: globals.timeout,
    requestId: globals.requestId || randomUUID(),
  };
}

export function resolveScheme(ctx: RuntimeContext, explicit: string | undefined, must = false): string | null {
  const scheme = explicit || ctx.scheme;
  if (must && !scheme) throw new CliError("CLI 参数错误", "SCHEME_REQUIRED", "missing scheme; use --scheme", 2);
  return scheme ?? null;
}
