import type { PilotConfigChangeClass, PilotConfigSnapshot } from "./types.js";

export function diffConfigSnapshots(
  previous: PilotConfigSnapshot,
  next: PilotConfigSnapshot,
): string[] {
  return diffValues(previous.config, next.config);
}

export function classifyConfigChanges(changedPaths: string[]): PilotConfigChangeClass[] {
  const classes = new Set<PilotConfigChangeClass>();

  for (const path of changedPaths) {
    classes.add(classifyPath(path));
  }

  return [...classes];
}

function classifyPath(path: string): PilotConfigChangeClass {
  if (path.startsWith("agent.") || path.startsWith("model.")) {
    return "next-request";
  }
  if (path === "extension.includeHookEvents") {
    return "runtime-live";
  }
  if (path.startsWith("extension.")) {
    return "next-runtime";
  }
  if (path.startsWith("router.")) {
    return classifyRouterPath(path);
  }
  if (path.startsWith("alwaysOn.")) {
    return "next-runtime";
  }
  if (path.startsWith("cron.")) {
    return "next-runtime";
  }
  if (path.startsWith("tools.")) {
    return "next-runtime";
  }
  return "next-runtime";
}

function classifyRouterPath(path: string): PilotConfigChangeClass {
  if (
    path.startsWith("router.scenarios.") ||
    path.startsWith("router.fallback.") ||
    path.startsWith("router.tokenSaver.tiers.") ||
    path === "router.zeroUsageRetry.maxAttempts" ||
    path === "router.zeroUsageRetry.enabled"
  ) {
    return "next-request";
  }
  if (path.startsWith("router.tokenSaver.judge")) {
    return "runtime-live";
  }
  if (path === "router.autoOrchestrate.skillExtensionId") {
    return "next-runtime";
  }
  if (
    path === "router.stats.enabled" ||
    path === "router.customRouter.extensionId"
  ) {
    return "restart-required";
  }
  return "next-runtime";
}

function diffValues(left: unknown, right: unknown, prefix = ""): string[] {
  if (Object.is(left, right)) {
    return [];
  }

  if (!isDiffableObject(left) || !isDiffableObject(right)) {
    return [prefix || "<root>"];
  }

  const keys = new Set([...Object.keys(left), ...Object.keys(right)]);
  const changes: string[] = [];
  for (const key of [...keys].sort()) {
    const path = prefix ? `${prefix}.${key}` : key;
    changes.push(...diffValues(left[key], right[key], path));
  }
  return changes;
}

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