import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { basename, extname, join, relative } from "node:path";
import type { DevToolbarPanel, DevToolbarPlatformSnapshot } from "../types.ts";

function walk(root: string, test: (file: string) => boolean): string[] {
  if (!existsSync(root)) return [];
  const result: string[] = [];
  for (const entry of readdirSync(root, { withFileTypes: true })) {
    const file = join(root, entry.name);
    if (entry.isDirectory()) result.push(...walk(file, test));
    else if (test(file)) result.push(file);
  }
  return result;
}

function flatten(value: unknown, prefix = ""): string[] {
  if (!value || typeof value !== "object" || Array.isArray(value)) return prefix ? [prefix] : [];
  return Object.entries(value).flatMap(([key, child]) =>
    flatten(child, prefix ? `${prefix}.${key}` : key),
  );
}

export interface BuiltinPanelOptions {
  root: string;
  platform?: DevToolbarPlatformSnapshot;
  database?: { queries: unknown[]; issues: unknown[] };
  version?: { current: string; latest?: string };
}

/** Produce bounded, credential-free first-party inspector data. */
export function builtinDevToolbarPanels(options: BuiltinPanelOptions): DevToolbarPanel[] {
  const app = join(options.root, "app");
  const queueFiles = walk(join(app, "queues"), (file) => /\.[cm]?[jt]s$/.test(file));
  const localeFiles = walk(join(app, "locales"), (file) => extname(file) === ".json");
  const locales = localeFiles.map((file) => {
    try {
      return {
        name: basename(file, ".json"),
        keys: flatten(JSON.parse(readFileSync(file, "utf8"))),
      };
    } catch {
      return { name: basename(file, ".json"), keys: [] as string[], invalid: true };
    }
  });
  const allKeys = new Set(locales.flatMap((locale) => locale.keys));
  const translations = locales.map((locale) => ({
    ...locale,
    missing: [...allKeys].filter((key) => !locale.keys.includes(key)).slice(0, 100),
  }));
  const styleFiles = walk(app, (file) => /\.(?:css|wrn)$/.test(file));
  const cssBytes = styleFiles.reduce((sum, file) => sum + statSync(file).size, 0);
  const memory = typeof process.memoryUsage === "function" ? process.memoryUsage() : undefined;
  const database = options.database ?? { queries: [], issues: [] };
  const realtimeMonitor = (globalThis as typeof globalThis & { __wrnexusRealtimeMonitor?: unknown })
    .__wrnexusRealtimeMonitor;
  const latest = options.version?.latest;
  return [
    {
      id: "database",
      title: "SQL",
      category: "database",
      badge: database.issues.length,
      data: { recent: database.queries.slice(-100), issues: database.issues.slice(-100) },
    },
    {
      id: "queues",
      title: "Queues",
      category: "queues",
      badge: queueFiles.length,
      data: {
        definitions: queueFiles.map((file) => relative(options.root, file).replace(/\\/g, "/")),
      },
    },
    {
      id: "realtime",
      title: "Realtime",
      category: "realtime",
      badge: options.platform?.routes?.realtime ?? 0,
      data: {
        routes: options.platform?.routes?.realtime ?? 0,
        monitor: realtimeMonitor ?? { rooms: 0, messages: 0, acknowledgements: 0 },
      },
    },
    {
      id: "translations",
      title: "Translations",
      category: "translations",
      badge: translations.reduce((sum, locale) => sum + locale.missing.length, 0),
      data: { locales: translations },
    },
    {
      id: "css",
      title: "CSS",
      category: "css",
      badge: styleFiles.length,
      data: { files: styleFiles.length, bytes: cssBytes, browserUnusedSelectorScan: true },
    },
    {
      id: "performance",
      title: "Memory & Vitals",
      category: "performance",
      data: { memory, webVitalsEndpoint: "/__wrnexus/metrics/vitals" },
    },
    {
      id: "upgrades",
      title: "Upgrades",
      category: "upgrades",
      badge: latest && latest !== options.version?.current ? 1 : 0,
      data: { current: options.version?.current, latest, updateCommand: "wrnexus update --latest" },
    },
  ];
}
