import { existsSync, rmSync } from "node:fs";
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, test } from "vitest";
import type { DashboardDocument } from "@dimensionlab/dashboard-model";
import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import { createDashboardStore, type DashboardStore } from "$lib/server/db/dashboard-store";
import {
  AgentConfigAuthorizationError,
  authorizeAgentConfigRequest,
  handleAgentDashboardRequest,
  previewDashboardChanges,
  publishDashboardChanges,
  rollbackDashboardRevision,
  type AgentDashboardOperation,
  type JsonPatchOperation,
} from ".";

const stores: DashboardStore[] = [];
const tempRoots: string[] = [];

afterEach(() => {
  stores.splice(0).forEach((store) => store.close());
  tempRoots.splice(0).forEach((path) => rmSync(path, { force: true, recursive: true }));
});

describe("agent dashboard configuration API", () => {
  test("previews typed dashboard operations with an RFC 6902-compatible patch", () => {
    const operations = exampleOperations();

    const result = previewDashboardChanges(genericDashboardFixture, operations);

    expect(result.ok).toBe(true);
    if (!result.ok) throw new Error("expected preview success");
    expect(result.document.serviceGroups[0]?.id).toBe("edge");
    expect(result.document.serviceGroups[0]?.services[0]).toMatchObject({
      id: "edge-router",
      datasource: {
        type: "external",
        adapter: "http-status",
        reference: "GET https://edge.example.test/api/status",
      },
    });
    expect(result.document.layout.telemetry).toEqual([
      "service-uptime",
      "edge-latency",
      "queue-depth",
    ]);
    expect(genericDashboardFixture.serviceGroups.map((group) => group.id)).toEqual([
      "core-services",
    ]);
    expect(result.patch.length).toBeGreaterThan(0);
    expect(result.patch.every((operation) => operation.path.startsWith("/"))).toBe(true);
    expect(result.patch.map((operation) => operation.op)).toContain("add");
  });

  test("returns structured repairable errors for invalid operations", () => {
    const result = previewDashboardChanges(genericDashboardFixture, [
      {
        type: "add_service",
        groupId: "missing-group",
        service: exampleService(),
      },
    ]);

    expect(result.ok).toBe(false);
    if (result.ok) throw new Error("expected preview failure");
    expect(result.errors).toEqual([
      expect.objectContaining({
        code: "group_not_found",
        operationIndex: 0,
        path: "/serviceGroups",
      }),
    ]);
  });

  test("rejects unsupported target-specific mutations", () => {
    const datasourceResult = previewDashboardChanges(genericDashboardFixture, [
      {
        type: "connect_datasource",
        target: { kind: "statusItem", stripId: "runtime", id: "status" },
        datasource: {
          type: "external",
          adapter: "http-status",
          reference: "GET https://status.example.test/api",
        },
      },
    ]);
    expect(datasourceResult.ok).toBe(false);
    if (datasourceResult.ok) throw new Error("expected datasource failure");
    expect(datasourceResult.errors[0]).toMatchObject({
      code: "unsupported_target",
      operationIndex: 0,
      path: "/statusStrips",
    });

    const thresholdResult = previewDashboardChanges(genericDashboardFixture, [
      {
        type: "set_status_rule",
        target: { kind: "service", id: "identity" },
        thresholds: { warning: 1 },
      },
    ]);
    expect(thresholdResult.ok).toBe(false);
    if (thresholdResult.ok) throw new Error("expected threshold failure");
    expect(thresholdResult.errors[0]).toMatchObject({
      code: "unsupported_target",
      operationIndex: 0,
      path: "/serviceGroups",
    });
  });

  test("removes status strip items through the shared remove operation", () => {
    const result = previewDashboardChanges(genericDashboardFixture, [
      {
        type: "remove_item",
        target: { kind: "statusItem", stripId: "runtime", id: "sync" },
      },
    ]);

    expect(result.ok).toBe(true);
    if (!result.ok) throw new Error("expected remove success");
    expect(result.document.statusStrips[0]?.items.map((item) => item.id)).toEqual([
      "status",
    ]);
    expect(result.patch.map((operation) => operation.op)).toContain("remove");
  });

  test("returns RFC 6902-applicable patches for multiple array removals", () => {
    const result = previewDashboardChanges(genericDashboardFixture, [
      {
        type: "remove_item",
        target: { kind: "statusItem", stripId: "runtime", id: "status" },
      },
      {
        type: "remove_item",
        target: { kind: "statusItem", stripId: "runtime", id: "sync" },
      },
    ]);

    expect(result.ok).toBe(true);
    if (!result.ok) throw new Error("expected remove success");
    expect(
      result.patch
        .filter((operation) => operation.op === "remove")
        .map((operation) => operation.path),
    ).toEqual(["/statusStrips/0/items/1", "/statusStrips/0/items/0"]);
    expect(applyJsonPatch(genericDashboardFixture, result.patch)).toMatchObject({
      statusStrips: [{ items: [] }],
    });
  });

  test("rejects ambiguous service and status targets", () => {
    const duplicateDocument = documentWithDuplicateNestedIds();

    const connect = previewDashboardChanges(duplicateDocument, [
      {
        type: "connect_datasource",
        target: { kind: "service", id: "identity" },
        datasource: {
          type: "external",
          adapter: "http-status",
          reference: "GET https://identity.example.test/health",
        },
      },
    ]);
    expect(connect.ok).toBe(false);
    if (connect.ok) throw new Error("expected ambiguous service failure");
    expect(connect.errors[0]).toMatchObject({
      code: "ambiguous_target",
      operationIndex: 0,
      path: "/serviceGroups",
    });

    const remove = previewDashboardChanges(duplicateDocument, [
      {
        type: "remove_item",
        target: { kind: "service", id: "identity" },
      },
    ]);
    expect(remove.ok).toBe(false);
    if (remove.ok) throw new Error("expected ambiguous removal failure");
    expect(remove.errors[0]).toMatchObject({
      code: "ambiguous_target",
      operationIndex: 0,
      path: "/serviceGroups",
    });

    const status = previewDashboardChanges(duplicateDocument, [
      {
        type: "set_status_rule",
        target: { kind: "statusItem", id: "status" },
        value: "Healthy",
      },
    ]);
    expect(status.ok).toBe(false);
    if (status.ok) throw new Error("expected ambiguous status failure");
    expect(status.errors[0]).toMatchObject({
      code: "ambiguous_target",
      operationIndex: 0,
      path: "/statusStrips",
    });
  });

  test("rejects create_dashboard when it is not the first operation", () => {
    const result = previewDashboardChanges(genericDashboardFixture, [
      {
        type: "remove_item",
        target: { kind: "telemetry", id: "queue-depth" },
      },
      {
        type: "create_dashboard",
        document: genericDashboardFixture,
      },
    ]);

    expect(result.ok).toBe(false);
    if (result.ok) throw new Error("expected sequence failure");
    expect(result.errors[0]).toMatchObject({
      code: "invalid_operation_sequence",
      operationIndex: 1,
      path: "/1",
    });
  });

  test("publishes valid operations as a persisted dashboard revision", async () => {
    const { dbPath, store } = await createTestStore();
    const seed = store.seedDashboardIfEmpty(genericDashboardFixture, {
      actor: "seed",
      message: "initial dashboard",
    });

    const result = publishDashboardChanges(store, exampleOperations(), {
      actor: "agent",
      message: "add edge router",
    });

    expect(result.ok).toBe(true);
    if (!result.ok) throw new Error("expected publish success");
    expect(existsSync(dbPath)).toBe(true);
    expect(result.revision.operation).toBe("commit");
    expect(result.revision.actor).toBe("agent");
    expect(result.revision.message).toBe("add edge router");
    expect(result.previousRevisionId).toBe(seed.id);
    expect(result.patch.length).toBeGreaterThan(0);
    expect(store.getActiveDashboard()?.document.serviceGroups[0]?.id).toBe("edge");
    expect(store.listRevisions()).toHaveLength(2);
  });

  test("does not publish invalid operations", async () => {
    const { store } = await createTestStore();
    const seed = store.seedDashboardIfEmpty(genericDashboardFixture, {
      actor: "seed",
    });

    const result = publishDashboardChanges(store, [
      {
        type: "remove_item",
        target: { kind: "telemetry", id: "missing-metric" },
      },
    ]);

    expect(result.ok).toBe(false);
    if (result.ok) throw new Error("expected publish failure");
    expect(result.errors[0]).toMatchObject({
      code: "item_not_found",
      operationIndex: 0,
    });
    expect(store.getActiveDashboard()?.currentRevisionId).toBe(seed.id);
    expect(store.listRevisions()).toHaveLength(1);
  });

  test("rolls back through the agent-safe revision path", async () => {
    const { store } = await createTestStore();
    const seed = store.seedDashboardIfEmpty(genericDashboardFixture, {
      actor: "seed",
    });
    const publish = publishDashboardChanges(store, exampleOperations(), {
      actor: "agent",
    });
    expect(publish.ok).toBe(true);

    const rollback = rollbackDashboardRevision(store, seed.id, {
      actor: "agent",
      message: "restore previous dashboard",
    });

    expect(rollback.operation).toBe("rollback");
    expect(rollback.actor).toBe("agent");
    expect(rollback.sourceRevisionId).toBe(seed.id);
    expect(store.getActiveDashboard()?.document.metadata.title).toBe("Operations Console");
    expect(store.listRevisions().map((revision) => revision.operation)).toEqual([
      "rollback",
      "commit",
      "seed",
    ]);
  });

  test("authenticates agent configuration requests with a shared token", () => {
    const authorized = new Request("https://dimensionlab.test/api/agent/dashboard", {
      headers: { authorization: "Bearer shared-secret" },
    });
    const rejected = new Request("https://dimensionlab.test/api/agent/dashboard");

    expect(authorizeAgentConfigRequest(authorized, "shared-secret")).toEqual({
      ok: true,
    });
    expect(() => authorizeAgentConfigRequest(rejected, "shared-secret")).toThrow(
      AgentConfigAuthorizationError,
    );
    expect(() => authorizeAgentConfigRequest(authorized, "")).toThrow(
      AgentConfigAuthorizationError,
    );
  });

  test("handles preview, publish, and rollback requests through the HTTP adapter", async () => {
    const { store } = await createTestStore();
    const seed = store.seedDashboardIfEmpty(genericDashboardFixture, {
      actor: "seed",
    });

    const preview = await handleAgentDashboardRequest(
      jsonRequest({
        action: "preview_changes",
        operations: exampleOperations(),
      }),
      { store, token: "shared-secret" },
    );
    expect(preview.status).toBe(200);
    expect(await preview.json()).toMatchObject({
      ok: true,
      action: "preview_changes",
      patch: expect.any(Array),
    });
    expect(store.listRevisions()).toHaveLength(1);

    const publish = await handleAgentDashboardRequest(
      jsonRequest({
        action: "publish_changes",
        actor: "agent",
        message: "publish edge router",
        operations: exampleOperations(),
      }),
      { store, token: "shared-secret" },
    );
    expect(publish.status).toBe(200);
    expect(await publish.json()).toMatchObject({
      ok: true,
      action: "publish_changes",
      revision: { operation: "commit", actor: "agent" },
    });
    expect(store.listRevisions()).toHaveLength(2);

    const rollback = await handleAgentDashboardRequest(
      jsonRequest({
        action: "rollback_revision",
        actor: "agent",
        revisionId: seed.id,
      }),
      { store, token: "shared-secret" },
    );
    expect(rollback.status).toBe(200);
    expect(await rollback.json()).toMatchObject({
      ok: true,
      action: "rollback_revision",
      revision: { operation: "rollback", sourceRevisionId: seed.id },
    });
  });

  test("returns a structured error for unknown rollback revisions", async () => {
    const { store } = await createTestStore();
    store.seedDashboardIfEmpty(genericDashboardFixture, {
      actor: "seed",
    });

    const rollback = await handleAgentDashboardRequest(
      jsonRequest({
        action: "rollback_revision",
        actor: "agent",
        revisionId: "missing-revision",
      }),
      { store, token: "shared-secret" },
    );

    expect(rollback.status).toBe(404);
    expect(await rollback.json()).toMatchObject({
      ok: false,
      errors: [
        {
          code: "revision_not_found",
          path: "/revisionId",
        },
      ],
    });
  });

  test("previews create_dashboard requests against an empty store", async () => {
    const { store } = await createTestStore();

    const preview = await handleAgentDashboardRequest(
      jsonRequest({
        action: "preview_changes",
        operations: [
          {
            type: "create_dashboard",
            document: genericDashboardFixture,
          },
        ],
      }),
      { store, token: "shared-secret" },
    );

    expect(preview.status).toBe(200);
    expect(await preview.json()).toMatchObject({
      ok: true,
      action: "preview_changes",
      document: {
        metadata: {
          title: "Operations Console",
        },
      },
    });
    expect(store.listRevisions()).toHaveLength(0);
  });
});

async function createTestStore() {
  const root = await mkdtemp(join(tmpdir(), "dimensionlab-agent-config-"));
  tempRoots.push(root);
  const dbPath = join(root, "dashboard.sqlite");
  const store = createDashboardStore({
    databaseUrl: `file:${dbPath}`,
  });
  stores.push(store);

  return { dbPath, store };
}

function exampleOperations(): AgentDashboardOperation[] {
  return [
    {
      type: "add_section",
      section: {
        id: "edge",
        title: "Edge",
        layout: "list",
      },
    },
    {
      type: "add_service",
      groupId: "edge",
      service: exampleService(),
    },
    {
      type: "add_metric_card",
      card: {
        id: "edge-latency",
        label: "Edge Latency",
        value: { kind: "latency", value: 12, precision: 0 },
        severity: "ok",
        detail: "p95",
        datasource: {
          type: "external",
          adapter: "prometheus",
          reference: "histogram_quantile(0.95, edge_request_duration_seconds_bucket)",
        },
      },
      position: { afterId: "service-uptime" },
    },
    {
      type: "connect_datasource",
      target: {
        kind: "service",
        groupId: "edge",
        id: "edge-router",
      },
      datasource: {
        type: "external",
        adapter: "http-status",
        reference: "GET https://edge.example.test/api/status",
      },
    },
    {
      type: "set_status_rule",
      target: { kind: "telemetry", id: "edge-latency" },
      thresholds: { warning: 50, danger: 100 },
    },
    {
      type: "arrange_item",
      area: "serviceGroups",
      id: "edge",
      index: 0,
    },
  ];
}

function exampleService() {
  return {
    id: "edge-router",
    label: "Edge Router",
    description: "Ingress and routing",
    icon: "mdi:router-network",
    severity: "ok" as const,
    detail: "pending datasource",
    datasource: { type: "placeholder" as const, reason: "health adapter pending" },
    link: {
      href: "https://edge.example.test",
      label: "Open Edge Router",
      external: true,
    },
  };
}

function documentWithDuplicateNestedIds(): DashboardDocument {
  const document = structuredClone(genericDashboardFixture);
  document.layout.serviceGroups.push("secondary-services");
  document.serviceGroups.push({
    id: "secondary-services",
    title: "Secondary Services",
    layout: "list",
    services: [
      {
        ...document.serviceGroups[0].services[0],
        label: "Shadow Identity",
      },
    ],
  });
  document.layout.statusStrips.push("secondary-runtime");
  document.statusStrips.push({
    id: "secondary-runtime",
    items: [
      {
        ...document.statusStrips[0].items[0],
        value: "Healthy",
      },
    ],
  });
  return document;
}

function applyJsonPatch<T>(value: T, patch: JsonPatchOperation[]): T {
  const next = structuredClone(value);
  for (const operation of patch) {
    const { parent, key } = jsonPointerTarget(next, operation.path);
    if (operation.op === "remove") {
      if (Array.isArray(parent)) {
        parent.splice(Number(key), 1);
      } else {
        delete parent[key];
      }
    } else if (operation.op === "add") {
      if (Array.isArray(parent)) {
        parent.splice(Number(key), 0, operation.value);
      } else {
        parent[key] = operation.value;
      }
    } else if (operation.op === "replace") {
      if (Array.isArray(parent)) {
        parent[Number(key)] = operation.value;
      } else {
        parent[key] = operation.value;
      }
    }
  }
  return next;
}

function jsonPointerTarget(value: unknown, path: string) {
  const segments = path
    .split("/")
    .slice(1)
    .map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
  const key = segments.pop();
  if (key === undefined) throw new Error(`Invalid JSON pointer: ${path}`);

  let parent = value as Record<string, unknown> | unknown[];
  for (const segment of segments) {
    parent = Array.isArray(parent)
      ? (parent[Number(segment)] as Record<string, unknown> | unknown[])
      : (parent[segment] as Record<string, unknown> | unknown[]);
  }
  return { parent, key };
}

function jsonRequest(body: unknown) {
  return new Request("https://dimensionlab.test/api/agent/dashboard", {
    body: JSON.stringify(body),
    headers: {
      authorization: "Bearer shared-secret",
      "content-type": "application/json",
    },
    method: "POST",
  });
}
