import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { resolvePilotHome } from "../pilot/paths.js";
import type { PermissionRule, PermissionRuleSet } from "./protocol/types.js";

export type PermissionSettings = {
  version: 1;
  allowedTools: string[];
  disallowedTools: string[];
  skipPermissions: boolean;
  lastUpdated?: string;
};

export const DEFAULT_PERMISSION_SETTINGS: PermissionSettings = {
  version: 1,
  allowedTools: [],
  disallowedTools: [],
  skipPermissions: true,
};

const TOOL_NAME_ALIASES = new Map<string, string>([
  ["Read", "read_file"],
  ["Write", "write_file"],
  ["Edit", "edit_file"],
  ["NotebookEdit", "edit_notebook"],
  ["MultiEdit", "edit_file"],
  ["Glob", "glob"],
  ["Grep", "grep"],
  ["Bash", "bash"],
  ["Task", "agent"],
  ["TodoWrite", "todo_write"],
  ["WebFetch", "web_fetch"],
  ["WebSearch", "web_search"],
]);

export function getPermissionSettingsPath(env: NodeJS.ProcessEnv = process.env): string {
  return join(resolvePilotHome(env), "permissions.json");
}

export function readPermissionSettings(env: NodeJS.ProcessEnv = process.env): PermissionSettings {
  try {
    const raw = readFileSync(getPermissionSettingsPath(env), "utf8");
    const parsed = JSON.parse(raw) as unknown;
    return normalizePermissionSettings(parsed);
  } catch {
    return { ...DEFAULT_PERMISSION_SETTINGS };
  }
}

export function writePermissionSettings(
  settings: Partial<PermissionSettings>,
  env: NodeJS.ProcessEnv = process.env,
): PermissionSettings {
  const next = normalizePermissionSettings({
    ...readPermissionSettings(env),
    ...settings,
    lastUpdated: new Date().toISOString(),
  });
  const filePath = getPermissionSettingsPath(env);
  mkdirSync(dirname(filePath), { recursive: true });
  writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
  return next;
}

export function permissionSettingsToRuleSet(settings: PermissionSettings): PermissionRuleSet {
  return {
    allow: settings.allowedTools.map((entry) => permissionEntryToRule(entry, "allow")),
    deny: settings.disallowedTools.map((entry) => permissionEntryToRule(entry, "deny")),
    ask: [],
  };
}

export function normalizePermissionEntry(entry: string): string {
  const trimmed = entry.trim();
  if (!trimmed) return "";

  const bashMatch = /^Bash\((.*)\)$/.exec(trimmed);
  if (bashMatch) {
    const pattern = bashMatch[1]?.trim();
    return pattern ? `bash:${pattern}` : "bash";
  }

  return TOOL_NAME_ALIASES.get(trimmed) ?? trimmed;
}

export function normalizePermissionSettings(value: unknown): PermissionSettings {
  const record = isRecord(value) ? value : {};
  return {
    version: 1,
    allowedTools: normalizeStringArray(record.allowedTools),
    disallowedTools: normalizeStringArray(record.disallowedTools),
    skipPermissions: Boolean(record.skipPermissions),
    lastUpdated: typeof record.lastUpdated === "string" ? record.lastUpdated : undefined,
  };
}

export function permissionEntryToRule(
  entry: string,
  behavior: "allow" | "deny",
  source: PermissionRule["source"] = "user",
): PermissionRule {
  const normalized = normalizePermissionEntry(entry);
  const [toolName, ...patternParts] = normalized.split(":");
  const pattern = patternParts.join(":").trim();
  return {
    source,
    behavior,
    toolName: toolName || normalized,
    pattern: pattern || undefined,
  };
}

function normalizeStringArray(value: unknown): string[] {
  if (!Array.isArray(value)) return [];
  const out: string[] = [];
  const seen = new Set<string>();
  for (const item of value) {
    if (typeof item !== "string") continue;
    const normalized = normalizePermissionEntry(item);
    if (!normalized || seen.has(normalized)) continue;
    seen.add(normalized);
    out.push(normalized);
  }
  return out;
}

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