import {
  readFileSync,
  existsSync,
} from "node:fs";
import { join, dirname } from "node:path";
import { ensureSecureDir, writeSecureFile } from "./fs-permissions.js";
import { ZENCODE_DIR } from "./config.js";
import { DEFAULT_QUALITY_GATE_CONFIG } from "./quality-gate.js";
import type { QualityGateConfig } from "./quality-gate.js";

export const DEFAULT_OPENROUTER_MODEL = "openrouter/free";

export interface SessionDefaults {
  model?: string;
  permissionMode?: string;
  enableFileCheckpointing?: boolean;
  maxTurns?: number;
  maxBudgetUsd?: number;
  systemPrompt?: string;
  appendSystemPrompt?: string;
  thinking?: { type: "adaptive" } | { type: "enabled"; budgetTokens: number } | { type: "disabled" };
  effort?: "low" | "medium" | "high" | "max";
  disallowedTools?: string[];
  additionalDirectories?: string[];
  betas?: string[];
  language?: string;
}

export interface ZencodeSettings {
  openrouterApiKey: string;
  openrouterModel: string;
  linearApiKey: string;
  linearAutoTransition: boolean;
  linearAutoTransitionStateId: string;
  linearAutoTransitionStateName: string;
  editorTabEnabled: boolean;
  attribution: boolean;
  sessionDefaults: SessionDefaults;
  qualityGate: QualityGateConfig;
  updatedAt: number;
}

const DEFAULT_PATH = join(ZENCODE_DIR, "settings.json");

let loaded = false;
let filePath = DEFAULT_PATH;
let settings: ZencodeSettings = {
  openrouterApiKey: "",
  openrouterModel: DEFAULT_OPENROUTER_MODEL,
  linearApiKey: "",
  linearAutoTransition: false,
  linearAutoTransitionStateId: "",
  linearAutoTransitionStateName: "",
  editorTabEnabled: false,
  attribution: false,
  sessionDefaults: {},
  qualityGate: { ...DEFAULT_QUALITY_GATE_CONFIG },
  updatedAt: 0,
};

function normalizeSessionDefaults(raw: unknown): SessionDefaults {
  if (raw == null || typeof raw !== "object") return {};
  const d = raw as Record<string, unknown>;
  const result: SessionDefaults = {};
  if (typeof d.model === "string") result.model = d.model;
  if (typeof d.permissionMode === "string") result.permissionMode = d.permissionMode;
  if (typeof d.enableFileCheckpointing === "boolean") result.enableFileCheckpointing = d.enableFileCheckpointing;
  if (typeof d.maxTurns === "number" && Number.isInteger(d.maxTurns) && d.maxTurns >= 1) result.maxTurns = d.maxTurns;
  if (typeof d.maxBudgetUsd === "number" && d.maxBudgetUsd > 0) result.maxBudgetUsd = d.maxBudgetUsd;
  if (typeof d.systemPrompt === "string") result.systemPrompt = d.systemPrompt;
  if (typeof d.appendSystemPrompt === "string") result.appendSystemPrompt = d.appendSystemPrompt;
  if (d.thinking != null && typeof d.thinking === "object" && "type" in d.thinking) {
    const t = d.thinking as { type: string; budgetTokens?: number };
    if (t.type === "adaptive" || t.type === "disabled") {
      result.thinking = { type: t.type };
    } else if (t.type === "enabled" && typeof t.budgetTokens === "number") {
      result.thinking = { type: "enabled", budgetTokens: t.budgetTokens };
    }
  }
  if (typeof d.effort === "string" && ["low", "medium", "high", "max"].includes(d.effort)) {
    result.effort = d.effort as SessionDefaults["effort"];
  }
  if (Array.isArray(d.disallowedTools)) result.disallowedTools = d.disallowedTools.filter((v): v is string => typeof v === "string");
  if (Array.isArray(d.additionalDirectories)) result.additionalDirectories = d.additionalDirectories.filter((v): v is string => typeof v === "string");
  if (Array.isArray(d.betas)) result.betas = d.betas.filter((v): v is string => typeof v === "string");
  if (typeof d.language === "string") result.language = d.language;
  return result;
}

function normalizeQualityGate(raw: unknown): QualityGateConfig {
  if (raw == null || typeof raw !== "object") return { ...DEFAULT_QUALITY_GATE_CONFIG };
  const d = raw as Record<string, unknown>;
  const enabled = typeof d.enabled === "boolean" ? d.enabled : false;
  let checks = DEFAULT_QUALITY_GATE_CONFIG.checks;
  if (Array.isArray(d.checks)) {
    checks = d.checks
      .filter((c): c is Record<string, unknown> => c != null && typeof c === "object")
      .map((c) => ({
        name: typeof c.name === "string" ? c.name : "Check",
        command: typeof c.command === "string" ? c.command : "",
        enabled: typeof c.enabled === "boolean" ? c.enabled : true,
        timeoutSeconds: typeof c.timeoutSeconds === "number" && c.timeoutSeconds > 0 ? c.timeoutSeconds : 120,
      }));
  }
  return { enabled, checks };
}

function normalize(raw: Partial<ZencodeSettings> | null | undefined): ZencodeSettings {
  return {
    openrouterApiKey: typeof raw?.openrouterApiKey === "string" ? raw.openrouterApiKey : "",
    openrouterModel:
      typeof raw?.openrouterModel === "string" && raw.openrouterModel.trim()
        ? raw.openrouterModel
        : DEFAULT_OPENROUTER_MODEL,
    linearApiKey: typeof raw?.linearApiKey === "string" ? raw.linearApiKey : "",
    linearAutoTransition: typeof raw?.linearAutoTransition === "boolean" ? raw.linearAutoTransition : false,
    linearAutoTransitionStateId: typeof raw?.linearAutoTransitionStateId === "string" ? raw.linearAutoTransitionStateId : "",
    linearAutoTransitionStateName: typeof raw?.linearAutoTransitionStateName === "string" ? raw.linearAutoTransitionStateName : "",
    editorTabEnabled: typeof raw?.editorTabEnabled === "boolean" ? raw.editorTabEnabled : false,
    attribution: typeof raw?.attribution === "boolean" ? raw.attribution : false,
    sessionDefaults: normalizeSessionDefaults(raw?.sessionDefaults),
    qualityGate: normalizeQualityGate(raw?.qualityGate),
    updatedAt: typeof raw?.updatedAt === "number" ? raw.updatedAt : 0,
  };
}

function ensureLoaded(): void {
  if (loaded) return;
  try {
    if (existsSync(filePath)) {
      const raw = readFileSync(filePath, "utf-8");
      settings = normalize(JSON.parse(raw) as Partial<ZencodeSettings>);
    }
  } catch {
    settings = normalize(null);
  }
  loaded = true;
}

function persist(): void {
  ensureSecureDir(dirname(filePath));
  writeSecureFile(filePath, JSON.stringify(settings, null, 2));
}

export function getSettings(): ZencodeSettings {
  ensureLoaded();
  return { ...settings };
}

export function updateSettings(
  patch: Partial<Pick<ZencodeSettings, "openrouterApiKey" | "openrouterModel" | "linearApiKey" | "linearAutoTransition" | "linearAutoTransitionStateId" | "linearAutoTransitionStateName" | "editorTabEnabled" | "attribution" | "sessionDefaults" | "qualityGate">>,
): ZencodeSettings {
  ensureLoaded();

  // Shallow-merge sessionDefaults so individual fields can be updated/cleared
  let mergedSessionDefaults = settings.sessionDefaults;
  if (patch.sessionDefaults !== undefined) {
    mergedSessionDefaults = { ...settings.sessionDefaults };
    for (const [key, value] of Object.entries(patch.sessionDefaults)) {
      if (value === null) {
        delete (mergedSessionDefaults as Record<string, unknown>)[key];
      } else {
        (mergedSessionDefaults as Record<string, unknown>)[key] = value;
      }
    }
  }

  settings = normalize({
    openrouterApiKey: patch.openrouterApiKey ?? settings.openrouterApiKey,
    openrouterModel: patch.openrouterModel ?? settings.openrouterModel,
    linearApiKey: patch.linearApiKey ?? settings.linearApiKey,
    linearAutoTransition: patch.linearAutoTransition ?? settings.linearAutoTransition,
    linearAutoTransitionStateId: patch.linearAutoTransitionStateId ?? settings.linearAutoTransitionStateId,
    linearAutoTransitionStateName: patch.linearAutoTransitionStateName ?? settings.linearAutoTransitionStateName,
    editorTabEnabled: patch.editorTabEnabled ?? settings.editorTabEnabled,
    attribution: patch.attribution ?? settings.attribution,
    sessionDefaults: mergedSessionDefaults,
    qualityGate: patch.qualityGate ?? settings.qualityGate,
    updatedAt: Date.now(),
  });
  persist();
  return { ...settings };
}

export function _resetForTest(customPath?: string): void {
  loaded = false;
  filePath = customPath || DEFAULT_PATH;
  settings = normalize(null);
}
