import { readFile, writeFile, mkdir } from "node:fs/promises";
import { join } from "node:path";
import { randomUUID } from "node:crypto";
import type { ToolsMenuRegistry, ToolType, ToolTypeDefinition, ToolConfigField, ToolEntry, AppIcon } from "./types";

const TOOLS_MENU_DIR = process.env.TOOLS_MENU_DIR || "/opt/teletraan/tools-menu";
const REGISTRY_PATH = join(TOOLS_MENU_DIR, "registry.json");

// ─── Async Mutex for Write Safety ────────────────────────────────────
let writeQueue: Promise<void> = Promise.resolve();

function serialized<T>(fn: () => Promise<T>): Promise<T> {
  const result = writeQueue.then(fn);
  writeQueue = result.then(
    () => {},
    () => {}
  );
  return result;
}

// ─── Read / Write ────────────────────────────────────────────────────

export async function readToolsRegistry(): Promise<ToolsMenuRegistry> {
  try {
    const raw = await readFile(REGISTRY_PATH, "utf-8");
    return JSON.parse(raw) as ToolsMenuRegistry;
  } catch (err: unknown) {
    if (err instanceof SyntaxError) throw err;
    return { types: {}, typeDefinitions: {}, tools: {}, viewOrders: {} };
  }
}

async function writeToolsRegistry(registry: ToolsMenuRegistry): Promise<void> {
  await mkdir(TOOLS_MENU_DIR, { recursive: true });
  await writeFile(REGISTRY_PATH, JSON.stringify(registry, null, 2));
}

// ─── View Order Helper ──────────────────────────────────────────────

function removeToolFromViewOrders(registry: ToolsMenuRegistry, tool: ToolEntry): void {
  const key = `type:${tool.typeId}`;
  if (registry.viewOrders[key]) {
    registry.viewOrders[key] = registry.viewOrders[key].filter(id => id !== tool.id);
  }
}

// ─── Types CRUD ─────────────────────────────────────────────────────

export async function listTypes(): Promise<ToolType[]> {
  const registry = await readToolsRegistry();
  return Object.values(registry.types);
}

export async function getType(id: string): Promise<ToolType | null> {
  const registry = await readToolsRegistry();
  return registry.types[id] || null;
}

export function addType(data: {
  name: string;
  description?: string;
  icon?: AppIcon;
  configFields?: ToolConfigField[];
}): Promise<ToolType> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    const id = randomUUID();
    const toolType: ToolType = {
      id,
      name: data.name,
      description: data.description,
      icon: data.icon || { type: "lucide", value: "Wrench" },
    };
    registry.types[id] = toolType;
    registry.typeDefinitions[id] = {
      typeId: id,
      configFields: data.configFields || [],
    };
    await writeToolsRegistry(registry);
    return toolType;
  });
}

export function updateType(
  id: string,
  updates: { name?: string; description?: string; icon?: AppIcon }
): Promise<ToolType> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    const toolType = registry.types[id];
    if (!toolType) throw new Error(`Tool type "${id}" not found`);
    if (updates.name !== undefined) toolType.name = updates.name;
    if (updates.description !== undefined) toolType.description = updates.description;
    if (updates.icon !== undefined) toolType.icon = updates.icon;
    registry.types[id] = toolType;
    await writeToolsRegistry(registry);
    return toolType;
  });
}

export function deleteType(
  id: string,
  cascade: boolean = false
): Promise<{ deleted: boolean; affectedTools?: ToolEntry[] }> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    const toolType = registry.types[id];
    if (!toolType) throw new Error(`Tool type "${id}" not found`);

    const affected = Object.values(registry.tools).filter(t => t.typeId === id);
    if (affected.length > 0 && !cascade) {
      return { deleted: false, affectedTools: affected };
    }

    if (cascade) {
      for (const tool of affected) {
        removeToolFromViewOrders(registry, tool);
        delete registry.tools[tool.id];
      }
    }

    delete registry.types[id];
    delete registry.typeDefinitions[id];
    delete registry.viewOrders[`type:${id}`];
    await writeToolsRegistry(registry);
    return { deleted: true };
  });
}

// ─── Type Definitions CRUD ──────────────────────────────────────────

export async function getTypeDefinition(typeId: string): Promise<ToolTypeDefinition | null> {
  const registry = await readToolsRegistry();
  return registry.typeDefinitions[typeId] || null;
}

export function updateTypeDefinition(
  typeId: string,
  fields: ToolConfigField[]
): Promise<ToolTypeDefinition> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    if (!registry.types[typeId]) throw new Error(`Tool type "${typeId}" not found`);
    registry.typeDefinitions[typeId] = { typeId, configFields: fields };
    await writeToolsRegistry(registry);
    return registry.typeDefinitions[typeId];
  });
}

// ─── Tools CRUD ─────────────────────────────────────────────────────

export async function listTools(filters?: { typeId?: string }): Promise<ToolEntry[]> {
  const registry = await readToolsRegistry();
  let tools = Object.values(registry.tools);
  if (filters?.typeId) tools = tools.filter(t => t.typeId === filters.typeId);
  return tools;
}

export async function getTool(id: string): Promise<ToolEntry | null> {
  const registry = await readToolsRegistry();
  return registry.tools[id] || null;
}

export function addTool(data: {
  name: string;
  typeId: string;
  address?: string;
  description?: string;
  icon?: AppIcon;
  color?: string;
  config?: Record<string, string>;
  status?: "online" | "offline";
}): Promise<ToolEntry> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    if (!registry.types[data.typeId]) throw new Error(`Tool type "${data.typeId}" not found`);

    const id = randomUUID();
    const now = new Date().toISOString();
    const tool: ToolEntry = {
      id,
      name: data.name,
      typeId: data.typeId,
      address: data.address,
      description: data.description,
      icon: data.icon || { type: "lucide", value: "Wrench" },
      color: data.color || "#6366f1",
      config: data.config || {},
      status: data.status || "offline",
      createdAt: now,
      updatedAt: now,
    };

    registry.tools[id] = tool;

    const viewKey = `type:${data.typeId}`;
    if (!registry.viewOrders[viewKey]) registry.viewOrders[viewKey] = [];
    registry.viewOrders[viewKey].push(id);

    await writeToolsRegistry(registry);
    return tool;
  });
}

export function updateTool(
  id: string,
  updates: {
    name?: string;
    typeId?: string;
    address?: string;
    description?: string;
    icon?: AppIcon;
    color?: string;
    config?: Record<string, string>;
    status?: "online" | "offline";
  }
): Promise<ToolEntry> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    const tool = registry.tools[id];
    if (!tool) throw new Error(`Tool "${id}" not found`);

    const oldTypeId = tool.typeId;

    if (updates.name !== undefined) tool.name = updates.name;
    if (updates.address !== undefined) tool.address = updates.address;
    if (updates.description !== undefined) tool.description = updates.description;
    if (updates.icon !== undefined) tool.icon = updates.icon;
    if (updates.color !== undefined) tool.color = updates.color;
    if (updates.config !== undefined) tool.config = updates.config;
    if (updates.status !== undefined) tool.status = updates.status;

    if (updates.typeId !== undefined && updates.typeId !== oldTypeId) {
      if (!registry.types[updates.typeId]) throw new Error(`Tool type "${updates.typeId}" not found`);
      tool.typeId = updates.typeId;
      const oldKey = `type:${oldTypeId}`;
      const newKey = `type:${updates.typeId}`;
      if (registry.viewOrders[oldKey]) {
        registry.viewOrders[oldKey] = registry.viewOrders[oldKey].filter(i => i !== id);
      }
      if (!registry.viewOrders[newKey]) registry.viewOrders[newKey] = [];
      registry.viewOrders[newKey].push(id);
    }

    tool.updatedAt = new Date().toISOString();
    registry.tools[id] = tool;
    await writeToolsRegistry(registry);
    return tool;
  });
}

export function deleteTool(id: string): Promise<void> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    const tool = registry.tools[id];
    if (!tool) throw new Error(`Tool "${id}" not found`);

    removeToolFromViewOrders(registry, tool);
    delete registry.tools[id];
    await writeToolsRegistry(registry);
  });
}

// ─── View Orders ────────────────────────────────────────────────────

export async function getViewOrder(viewKey: string): Promise<string[]> {
  const registry = await readToolsRegistry();
  return registry.viewOrders[viewKey] || [];
}

export function updateViewOrder(viewKey: string, order: string[]): Promise<void> {
  return serialized(async () => {
    const registry = await readToolsRegistry();
    registry.viewOrders[viewKey] = order;
    await writeToolsRegistry(registry);
  });
}
