// packages/db/src/sync-capabilities.ts
// Reads platform tool metadata from platform-tools-snapshot.json and upserts into
// PlatformCapability table. Idempotent: safe to run on every deploy.
//
// The snapshot is generated by scripts/generate-tools-snapshot.js at build time
// from apps/web/lib/mcp-tools.ts. It contains name, description, and metadata flags
// for each platform tool.

import * as crypto from "crypto";
import { readFileSync } from "fs";
import { join } from "path";
import type { PrismaClient } from "../generated/client/client";
import { Prisma } from "../generated/client/client";

// ─── Types ────────────────────────────────────────────────────────────────────

type BuildPhaseTag = "ideate" | "plan" | "build" | "review" | "ship";

interface ToolSnapshot {
  name: string;
  description: string;
  sideEffect: boolean;
  requiresExternalAccess: boolean;
  buildPhases: BuildPhaseTag[] | null;
}

// ─── Helpers ─────────────────────────────────────────────────────────────────

function computeDefinitionHash(tool: ToolSnapshot): string {
  const payload = JSON.stringify({
    name: tool.name,
    description: tool.description,
  });
  return crypto.createHash("sha256").update(payload).digest("hex").slice(0, 16);
}

type RiskClass = "critical" | "elevated" | "standard";
type AuditClass = "ledger" | "journal" | "metrics_only";

function deriveRiskClass(tool: ToolSnapshot): RiskClass {
  if (tool.sideEffect && tool.requiresExternalAccess) return "critical";
  if (tool.sideEffect || tool.requiresExternalAccess) return "elevated";
  return "standard";
}

function deriveAuditClass(tool: ToolSnapshot): AuditClass {
  if (tool.sideEffect) return "ledger";
  if (tool.requiresExternalAccess) return "journal";
  return "metrics_only";
}

// ─── Snapshot loader ─────────────────────────────────────────────────────────

function loadToolsSnapshot(): ToolSnapshot[] {
  const snapshotPath = join(__dirname, "platform-tools-snapshot.json");
  const raw = readFileSync(snapshotPath, "utf8");
  return JSON.parse(raw) as ToolSnapshot[];
}

// ─── Main export ──────────────────────────────────────────────────────────────

export async function syncCapabilities(prisma: PrismaClient): Promise<void> {
  console.log("[sync-capabilities] Starting capability sync...");

  let tools: ToolSnapshot[];
  try {
    tools = loadToolsSnapshot();
    console.log(`[sync-capabilities] Loaded ${tools.length} tools from snapshot.`);
  } catch (err) {
    console.error(
      "[sync-capabilities] Failed to load tools snapshot — skipping sync:",
      err instanceof Error ? err.message : err
    );
    return; // fail-open
  }

  const seenCapabilityIds = new Set<string>();

  for (const tool of tools) {
    try {
      const capabilityId = `platform:${tool.name}`;
      seenCapabilityIds.add(capabilityId);

      const definitionHash = computeDefinitionHash(tool);
      const riskClass = deriveRiskClass(tool);
      const auditClass = deriveAuditClass(tool);

      const existing = await prisma.platformCapability.findUnique({
        where: { capabilityId },
        select: { manifest: true },
      });

      let schemaChangedAt: string | null = null;
      if (existing) {
        const prev = existing.manifest as Record<string, unknown>;
        if (prev.definitionHash !== definitionHash) {
          schemaChangedAt = new Date().toISOString();
          console.log(`[sync-capabilities] Schema changed: ${capabilityId}`);
        } else {
          schemaChangedAt = (prev.schemaChangedAt as string | null) ?? null;
        }
      }

      const manifest: Record<string, unknown> = {
        sourceType: "internal",
        riskClass,
        auditClass,
        adapterType: "platform_tool",
        requiresExternalAccess: tool.requiresExternalAccess,
        sideEffect: tool.sideEffect,
        buildPhases: tool.buildPhases,
        integrationDependencies: [],
        definitionHash,
        schemaChangedAt,
      };

      await prisma.platformCapability.upsert({
        where: { capabilityId },
        update: {
          name: tool.name,
          description: tool.description,
          state: "active",
          manifest: manifest as Prisma.InputJsonValue,
        },
        create: {
          capabilityId,
          name: tool.name,
          description: tool.description,
          state: "active",
          manifest: manifest as Prisma.InputJsonValue,
        },
      });
    } catch (err) {
      console.error(
        `[sync-capabilities] Error upserting ${tool.name}:`,
        err instanceof Error ? err.message : err
      );
    }
  }

  // Deprecate tools that no longer exist in the snapshot
  try {
    const allCapabilities = await prisma.platformCapability.findMany({
      where: { capabilityId: { startsWith: "platform:" } },
      select: { capabilityId: true, state: true },
    });

    for (const cap of allCapabilities) {
      if (!seenCapabilityIds.has(cap.capabilityId) && cap.state !== "deprecated") {
        console.warn(
          `[sync-capabilities] Tool removed from snapshot — deprecating: ${cap.capabilityId}`
        );
        await prisma.platformCapability.update({
          where: { capabilityId: cap.capabilityId },
          data: { state: "deprecated" },
        });
      }
    }
  } catch (err) {
    console.error(
      "[sync-capabilities] Error during deprecation sweep:",
      err instanceof Error ? err.message : err
    );
  }

  console.log(
    `[sync-capabilities] Done. Synced ${seenCapabilityIds.size} platform capabilities.`
  );
}
