import {
  SEAT_MODEL_ENV_KEYS,
  SEAT_ORCHESTRATION_ENV_KEYS,
} from "./seat-constants.mjs";

/** Non-authoritative keys only — proxy routing + benign locale/terminal. */
export const SEAT_ENV_ALLOWLIST = new Set([
  "PATH",
  "SHELL",
  "LANG",
  "LC_ALL",
  "LC_CTYPE",
  "LC_MESSAGES",
  "TERM",
  "ANTHROPIC_BASE_URL",
]);

/** Launch-authority values — root-validated scope/implementer-exec env only. */
export const SEAT_ENV_AUTHORITY_KEYS = new Set([
  "HOME",
  "USER",
  "LOGNAME",
  "CLAUDE_CONFIG_DIR",
  "CLAUDE_SECURESTORAGE_CONFIG_DIR",
  "SYSTRAY_CLAUDE_ACCOUNT_HOME",
  "SYSTRAY_CLAUDE_ACCOUNT_SLUG",
  "OVERDECK_SEAT_HOST",
  "OVERDECK_SEAT_ID",
  "OVERDECK_SEAT_MODEL",
  "OVERDECK_CLAUDE_BIN",
  "OVERDECK_SEAT_GUARD_BIN",
  "OVERDECK_SEAT_CHECKOUT",
  "OVERDECK_SEAT_STATE_DIR",
]);

const SHELL_HOME_MARKERS = ["$HOME", "$(", "`"];

function seatEnvValueHasShellHome(value) {
  const str = String(value);
  return SHELL_HOME_MARKERS.some((marker) => str.includes(marker));
}

export function extractSeatEnvJsonFields(env) {
  const out = {};
  for (const [key, value] of Object.entries(env || {})) {
    if (SEAT_ENV_ALLOWLIST.has(key)) out[key] = value;
  }
  return out;
}

export const SEAT_SANDBOX_WEAKEN_ENV_KEYS = [
  "CLAUDE_CODE_DISABLE_SANDBOX",
  "CLAUDE_CODE_SANDBOX_DISABLED",
  "CLAUDE_BASH_NO_SANDBOX",
  "ANTHROPIC_SANDBOX_DISABLED",
];

export const SEAT_ENV_CREDENTIAL_KEYS = new Set([
  "ANTHROPIC_API_KEY",
  "ANTHROPIC_AUTH_TOKEN",
  "OPENAI_API_KEY",
  "CCP_CONFIG_DIR",
  "CCP_BIND_ADDRESS",
  "CCP_CODEX_MODEL",
  "CCP_CODEX_SERVICE_TIER",
  "DBUS_SESSION_BUS_ADDRESS",
  "XDG_RUNTIME_DIR",
  ...SEAT_MODEL_ENV_KEYS.filter((k) => !["ANTHROPIC_BASE_URL"].includes(k)),
  ...SEAT_ORCHESTRATION_ENV_KEYS,
  ...SEAT_SANDBOX_WEAKEN_ENV_KEYS,
]);

const CONTROL_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/;

const FORWARDED_EXACT_DENY = new Set([
  "--model",
  "--fallback-model",
  "--settings",
  "--setting-sources",
  "--disallowedTools",
  "--allowedTools",
  "--disallowed-tools",
  "--allowed-tools",
  "--tools",
  "--agents",
  "--strict-mcp-config",
  "--mcp-config",
  "--plugin",
  "--plugins",
  "--plugin-dir",
  "--plugin-url",
  "--marketplace",
  "--enable-workflows",
  "--enable-workflow",
  "--agent",
  "--workflow",
  "--sandbox",
  "--no-sandbox",
  "Agent",
  "Workflow",
]);

const FORWARDED_PREFIX_DENY = [
  "--model=",
  "--fallback-model=",
  "--settings=",
  "--setting-sources=",
  "--disallowedTools=",
  "--allowedTools=",
  "--disallowed-tools=",
  "--allowed-tools=",
  "--tools=",
  "--agents=",
  "--strict-mcp-config=",
  "--mcp-config=",
  "--plugin=",
  "--plugins=",
  "--plugin-dir=",
  "--plugin-url=",
  "--marketplace=",
  "--enable-workflows=",
  "--enable-workflow=",
  "--agent=",
  "--workflow=",
  "--sandbox=",
  "--no-sandbox=",
];

export function validateSeatEnvObject(env) {
  const errors = [];
  const seen = new Set();
  for (const [key, value] of Object.entries(env || {})) {
    if (seen.has(key)) {
      errors.push(`duplicate:${key}`);
      continue;
    }
    seen.add(key);
    if (SEAT_ENV_AUTHORITY_KEYS.has(key)) {
      errors.push(`authority:${key}`);
      continue;
    }
    if (!SEAT_ENV_ALLOWLIST.has(key)) {
      errors.push(`unknown:${key}`);
      continue;
    }
    if (SEAT_ENV_CREDENTIAL_KEYS.has(key)) {
      errors.push(`credential:${key}`);
      continue;
    }
    if (value === undefined || value === null) {
      errors.push(`missing:${key}`);
      continue;
    }
    const str = String(value);
    if (CONTROL_RE.test(str)) errors.push(`control:${key}`);
    if (seatEnvValueHasShellHome(str)) errors.push(`shell-home:${key}`);
  }
  return { ok: errors.length === 0, errors };
}

export function buildSeatEnvJsonContent(env) {
  const check = validateSeatEnvObject(env);
  if (!check.ok) throw new Error(`seat-env-invalid:${check.errors.join(",")}`);
  return `${JSON.stringify(env, null, 2)}\n`;
}

export function validateForwardedArgv(argv) {
  const errors = [];
  if (!Array.isArray(argv)) return { ok: false, errors: ["not-array"] };
  for (let i = 0; i < argv.length; i += 1) {
    const arg = argv[i];
    if (typeof arg !== "string") {
      errors.push(`not-string:${i}`);
      continue;
    }
    if (CONTROL_RE.test(arg)) {
      errors.push(`control:${i}`);
      continue;
    }
    if (FORWARDED_EXACT_DENY.has(arg)) {
      errors.push(`deny:${arg}`);
      continue;
    }
    if (FORWARDED_PREFIX_DENY.some((prefix) => arg.startsWith(prefix))) {
      errors.push(`deny:${arg}`);
      continue;
    }
    if (arg === "--model" || arg === "--fallback-model" || arg === "--settings" || arg === "--mcp-config") {
      errors.push(`deny:${arg}`);
    }
  }
  return { ok: errors.length === 0, errors };
}

export const SEAT_ENV_JSON_LOADER_PY = `import json, sys
CONTROL = set(range(0, 9)) | {11, 12} | set(range(14, 32)) | {127}
ALLOW = ${JSON.stringify([...SEAT_ENV_ALLOWLIST])}
AUTHORITY = ${JSON.stringify([...SEAT_ENV_AUTHORITY_KEYS])}
DENY = ${JSON.stringify([...SEAT_ENV_CREDENTIAL_KEYS])}
SHELL_HOME_MARKERS = ("$HOME", "$(", chr(96))
allow = set(ALLOW)
authority = set(AUTHORITY)
deny = set(DENY)
path = sys.argv[1]
with open(path, encoding="utf-8") as fh:
    data = json.load(fh)
if not isinstance(data, dict):
    raise SystemExit("seat-env-not-object")
seen = set()
for key, value in data.items():
    if key in seen:
        raise SystemExit(f"seat-env-duplicate:{key}")
    seen.add(key)
    if key in authority:
        raise SystemExit(f"seat-env-reject-authority:{key}")
    if key not in allow or key in deny:
        raise SystemExit(f"seat-env-reject:{key}")
    if not isinstance(value, str):
        raise SystemExit(f"seat-env-type:{key}")
    if any(ord(ch) in CONTROL for ch in value):
        raise SystemExit(f"seat-env-control:{key}")
    if any(marker in value for marker in SHELL_HOME_MARKERS):
        raise SystemExit(f"seat-env-shell-home:{key}")
    sys.stdout.write(key)
    sys.stdout.write("\\0")
    sys.stdout.write(value)
    sys.stdout.write("\\0")
`;

export const SEAT_FORWARDED_JSON_LOADER_PY = `import json, sys
CONTROL = set(range(0, 9)) | {11, 12} | set(range(14, 32)) | {127}
DENY_EXACT = ${JSON.stringify([...FORWARDED_EXACT_DENY])}
DENY_PREFIX = ${JSON.stringify(FORWARDED_PREFIX_DENY)}
path = sys.argv[1]
with open(path, encoding="utf-8") as fh:
    data = json.load(fh)
if not isinstance(data, list):
    raise SystemExit("forwarded-not-array")
for idx, item in enumerate(data):
    if not isinstance(item, str):
        raise SystemExit(f"forwarded-not-string:{idx}")
    if any(ord(ch) in CONTROL for ch in item):
        raise SystemExit(f"forwarded-control:{idx}")
    if item in DENY_EXACT:
        raise SystemExit(f"forwarded-deny:{item}")
    for prefix in DENY_PREFIX:
        if item.startswith(prefix):
            raise SystemExit(f"forwarded-deny:{item}")
    sys.stdout.write(item)
    sys.stdout.write("\\0")
`;
