import Ajv, { type ErrorObject } from "ajv";
import addFormats from "ajv-formats";
import { Type, type Static } from "@sinclair/typebox";
import {
  DashboardDocumentSchema,
  DatasourceReferenceSchema,
  ServiceEntrySchema,
  TelemetryCardSchema,
  ThresholdSchema,
  validateDashboardDocument,
  type DashboardDocument,
  type DashboardModule,
  type DashboardValidationFailure,
  type DatasourceReference,
  type ServiceEntry,
  type ServiceGroup,
  type StatusItem,
  type TelemetryCard,
} from "@dimensionlab/dashboard-model";
import {
  createDashboardStore,
  DashboardRevisionNotFoundError,
  type DashboardRevision,
  type DashboardStore,
  type DashboardWriteMetadata,
} from "$lib/server/db/dashboard-store";

const IdentifierSchema = Type.String({
  minLength: 1,
  pattern: "^[a-z0-9][a-z0-9-_.:]*$",
});

const PositionSchema = Type.Object(
  {
    index: Type.Optional(Type.Integer({ minimum: 0 })),
    beforeId: Type.Optional(IdentifierSchema),
    afterId: Type.Optional(IdentifierSchema),
  },
  { additionalProperties: false, minProperties: 1 },
);

const AgentTargetSchema = Type.Union([
  Type.Object(
    {
      kind: Type.Literal("telemetry"),
      id: IdentifierSchema,
    },
    { additionalProperties: false },
  ),
  Type.Object(
    {
      kind: Type.Literal("service"),
      groupId: Type.Optional(IdentifierSchema),
      id: IdentifierSchema,
    },
    { additionalProperties: false },
  ),
  Type.Object(
    {
      kind: Type.Literal("serviceGroup"),
      id: IdentifierSchema,
    },
    { additionalProperties: false },
  ),
  Type.Object(
    {
      kind: Type.Literal("statusItem"),
      stripId: Type.Optional(IdentifierSchema),
      id: IdentifierSchema,
    },
    { additionalProperties: false },
  ),
  Type.Object(
    {
      kind: Type.Literal("module"),
      id: IdentifierSchema,
    },
    { additionalProperties: false },
  ),
]);

const AddSectionOperationSchema = Type.Object(
  {
    type: Type.Literal("add_section"),
    section: Type.Object(
      {
        id: IdentifierSchema,
        title: Type.String({ minLength: 1 }),
        layout: Type.Optional(Type.Union([Type.Literal("list"), Type.Literal("grid")])),
      },
      { additionalProperties: false },
    ),
    position: Type.Optional(PositionSchema),
  },
  { additionalProperties: false },
);

const AddServiceOperationSchema = Type.Object(
  {
    type: Type.Literal("add_service"),
    groupId: IdentifierSchema,
    service: ServiceEntrySchema,
    position: Type.Optional(PositionSchema),
  },
  { additionalProperties: false },
);

const AddMetricCardOperationSchema = Type.Object(
  {
    type: Type.Literal("add_metric_card"),
    card: TelemetryCardSchema,
    position: Type.Optional(PositionSchema),
  },
  { additionalProperties: false },
);

const ConnectDatasourceOperationSchema = Type.Object(
  {
    type: Type.Literal("connect_datasource"),
    target: AgentTargetSchema,
    datasource: DatasourceReferenceSchema,
  },
  { additionalProperties: false },
);

const SetStatusRuleOperationSchema = Type.Object(
  {
    type: Type.Literal("set_status_rule"),
    target: AgentTargetSchema,
    thresholds: Type.Optional(ThresholdSchema),
    severity: Type.Optional(
      Type.Union([
        Type.Literal("neutral"),
        Type.Literal("ok"),
        Type.Literal("warning"),
        Type.Literal("danger"),
        Type.Literal("stale"),
        Type.Literal("unavailable"),
      ]),
    ),
    detail: Type.Optional(Type.String()),
    value: Type.Optional(Type.String()),
  },
  { additionalProperties: false, minProperties: 3 },
);

const ArrangeItemOperationSchema = Type.Object(
  {
    type: Type.Literal("arrange_item"),
    area: Type.Union([
      Type.Literal("telemetry"),
      Type.Literal("serviceGroups"),
      Type.Literal("statusStrips"),
      Type.Literal("modules"),
    ]),
    id: IdentifierSchema,
    index: Type.Integer({ minimum: 0 }),
  },
  { additionalProperties: false },
);

const RemoveItemOperationSchema = Type.Object(
  {
    type: Type.Literal("remove_item"),
    target: AgentTargetSchema,
  },
  { additionalProperties: false },
);

const NestedDashboardDocumentSchema = Type.Unsafe<DashboardDocument>(
  schemaWithoutRootId(DashboardDocumentSchema),
);

const CreateDashboardOperationSchema = Type.Object(
  {
    type: Type.Literal("create_dashboard"),
    document: NestedDashboardDocumentSchema,
  },
  { additionalProperties: false },
);

export const AgentDashboardOperationSchema = Type.Union([
  CreateDashboardOperationSchema,
  AddSectionOperationSchema,
  AddServiceOperationSchema,
  AddMetricCardOperationSchema,
  ConnectDatasourceOperationSchema,
  SetStatusRuleOperationSchema,
  ArrangeItemOperationSchema,
  RemoveItemOperationSchema,
]);

export const AgentDashboardOperationsSchema = Type.Array(AgentDashboardOperationSchema, {
  minItems: 1,
});

const PreviewRequestSchema = Type.Object(
  {
    action: Type.Literal("preview_changes"),
    operations: AgentDashboardOperationsSchema,
  },
  { additionalProperties: false },
);

const PublishRequestSchema = Type.Object(
  {
    action: Type.Literal("publish_changes"),
    actor: Type.Optional(Type.String({ minLength: 1 })),
    message: Type.Optional(Type.String({ minLength: 1 })),
    operations: AgentDashboardOperationsSchema,
  },
  { additionalProperties: false },
);

const RollbackRequestSchema = Type.Object(
  {
    action: Type.Literal("rollback_revision"),
    actor: Type.Optional(Type.String({ minLength: 1 })),
    message: Type.Optional(Type.String({ minLength: 1 })),
    revisionId: Type.String({ minLength: 1 }),
  },
  { additionalProperties: false },
);

export const AgentDashboardRequestSchema = Type.Union([
  PreviewRequestSchema,
  PublishRequestSchema,
  RollbackRequestSchema,
]);

export type AgentDashboardOperation = Static<typeof AgentDashboardOperationSchema>;
export type AgentDashboardRequest = Static<typeof AgentDashboardRequestSchema>;

export interface JsonPatchAddOperation {
  op: "add";
  path: string;
  value: unknown;
}

export interface JsonPatchRemoveOperation {
  op: "remove";
  path: string;
}

export interface JsonPatchReplaceOperation {
  op: "replace";
  path: string;
  value: unknown;
}

export type JsonPatchOperation =
  | JsonPatchAddOperation
  | JsonPatchRemoveOperation
  | JsonPatchReplaceOperation;

export interface AgentConfigError {
  code: string;
  message: string;
  operationIndex?: number;
  path: string;
  details?: unknown;
}

export type AgentPreviewResult =
  | {
      ok: true;
      document: DashboardDocument;
      patch: JsonPatchOperation[];
    }
  | {
      ok: false;
      errors: AgentConfigError[];
    };

export type AgentPublishResult =
  | {
      ok: true;
      document: DashboardDocument;
      patch: JsonPatchOperation[];
      previousRevisionId: string | null;
      revision: DashboardRevision;
    }
  | {
      ok: false;
      errors: AgentConfigError[];
    };

export interface AgentDashboardHandlerOptions {
  store?: DashboardStore;
  token?: string;
}

export class AgentConfigAuthorizationError extends Error {
  constructor() {
    super("Unauthorized dashboard configuration request");
    this.name = "AgentConfigAuthorizationError";
  }
}

const validateOperations = createAjv().compile<AgentDashboardOperation[]>(
  AgentDashboardOperationsSchema,
);
const validateRequest = createAjv().compile<AgentDashboardRequest>(
  AgentDashboardRequestSchema,
);

function schemaWithoutRootId<T>(schema: T): T {
  const clone = JSON.parse(JSON.stringify(schema)) as T & { $id?: string };
  delete clone.$id;
  return clone;
}

function createAjv() {
  return addFormats(
    new Ajv({
      allErrors: true,
      strict: false,
      strictNumbers: true,
    }),
  );
}

export function previewDashboardChanges(
  document: DashboardDocument,
  operations: AgentDashboardOperation[],
): AgentPreviewResult {
  const operationValidation = validateAgentOperations(operations);
  if (operationValidation.length) return { ok: false, errors: operationValidation };

  const before = structuredClone(document);
  let next = structuredClone(document);
  const errors: AgentConfigError[] = [];

  operations.forEach((operation, operationIndex) => {
    if (errors.length) return;
    const result = applyOperation(next, operation, operationIndex);
    if (result.ok) {
      next = result.document;
    } else {
      errors.push(...result.errors);
    }
  });

  if (errors.length) return { ok: false, errors };

  const validation = validateDashboardDocument(next);
  if (!validation.valid) {
    return {
      ok: false,
      errors: dashboardValidationErrors(validation),
    };
  }

  return {
    ok: true,
    document: validation.data,
    patch: createJsonPatch(before, validation.data),
  };
}

export function publishDashboardChanges(
  store: DashboardStore,
  operations: AgentDashboardOperation[],
  metadata: DashboardWriteMetadata = {},
): AgentPublishResult {
  const active = store.getActiveDashboard();
  if (!active) {
    const create = operations[0];
    if (!create || create.type !== "create_dashboard") {
      return {
        ok: false,
        errors: [
          {
            code: "no_active_dashboard",
            message: "No active dashboard exists; start with create_dashboard",
            path: "/",
          },
        ],
      };
    }
  }

  const base = active?.document || emptyDashboardDocument();
  const preview = previewDashboardChanges(base, operations);
  if (!preview.ok) return preview;

  const revision = store.commitDashboard(preview.document, metadata);
  return {
    ok: true,
    document: revision.document,
    patch: preview.patch,
    previousRevisionId: active?.currentRevisionId || null,
    revision,
  };
}

export function rollbackDashboardRevision(
  store: DashboardStore,
  revisionId: string,
  metadata: DashboardWriteMetadata = {},
) {
  return store.rollbackToRevision(revisionId, metadata);
}

export function authorizeAgentConfigRequest(
  request: Request,
  token = process.env.AGENT_CONFIG_TOKEN,
): { ok: true } {
  if (!token) throw new AgentConfigAuthorizationError();

  const authorization = request.headers.get("authorization") || "";
  const bearer = authorization.match(/^Bearer\s+(.+)$/i)?.[1];
  const headerToken = request.headers.get("x-agent-config-token");
  if (bearer === token || headerToken === token) return { ok: true };

  throw new AgentConfigAuthorizationError();
}

export async function handleAgentDashboardRequest(
  request: Request,
  options: AgentDashboardHandlerOptions = {},
): Promise<Response> {
  const ownsStore = !options.store;
  const store = options.store || createDashboardStore();

  try {
    authorizeAgentConfigRequest(request, options.token);
    const body = await parseRequestBody(request);
    const requestValidationErrors = validateAgentRequest(body);
    if (requestValidationErrors.length) {
      return Response.json(
        { ok: false, errors: requestValidationErrors },
        { status: 400 },
      );
    }

    const agentRequest = body as AgentDashboardRequest;
    if (agentRequest.action === "rollback_revision") {
      const revision = rollbackDashboardRevision(store, agentRequest.revisionId, {
        actor: agentRequest.actor || "agent",
        message: agentRequest.message,
      });
      return Response.json({
        ok: true,
        action: agentRequest.action,
        revision: serializeRevision(revision),
      });
    }

    if (agentRequest.action === "preview_changes") {
      const active = store.getActiveDashboard();
      const base = active?.document || previewBaseDocument(agentRequest.operations);
      if (!base) {
        return Response.json(
          {
            ok: false,
            errors: [
              {
                code: "no_active_dashboard",
                message: "No active dashboard exists to preview against",
                path: "/",
              },
            ],
          },
          { status: 409 },
        );
      }
      const preview = previewDashboardChanges(base, agentRequest.operations);
      return Response.json(
        {
          ...preview,
          action: agentRequest.action,
        },
        { status: preview.ok ? 200 : 422 },
      );
    }

    const publish = publishDashboardChanges(store, agentRequest.operations, {
      actor: agentRequest.actor || "agent",
      message: agentRequest.message,
    });
    return Response.json(
      {
        ...publish,
        action: agentRequest.action,
        revision: publish.ok ? serializeRevision(publish.revision) : undefined,
      },
      { status: publish.ok ? 200 : 422 },
    );
  } catch (error) {
    if (error instanceof AgentConfigAuthorizationError) {
      return Response.json(
        {
          ok: false,
          errors: [
            {
              code: "unauthorized",
              message: error.message,
              path: "/",
            },
          ],
        },
        { status: 401 },
      );
    }

    if (error instanceof SyntaxError) {
      return Response.json(
        {
          ok: false,
          errors: [
            {
              code: "invalid_json",
              message: error.message,
              path: "/",
            },
          ],
        },
        { status: 400 },
      );
    }

    if (error instanceof DashboardRevisionNotFoundError) {
      return Response.json(
        {
          ok: false,
          errors: [
            {
              code: "revision_not_found",
              message: error.message,
              path: "/revisionId",
            },
          ],
        },
        { status: 404 },
      );
    }

    throw error;
  } finally {
    if (ownsStore) store.close();
  }
}

function validateAgentOperations(operations: unknown): AgentConfigError[] {
  if (!validateOperations(operations)) {
    return schemaErrors(validateOperations.errors || [], "invalid_operation_schema");
  }

  const createDashboardIndices = operations.flatMap((operation, index) =>
    operation.type === "create_dashboard" ? [index] : [],
  );
  if (createDashboardIndices.length > 1) {
    const operationIndex = createDashboardIndices[1];
    return [
      {
        code: "invalid_operation_sequence",
        message: "create_dashboard can only appear once",
        operationIndex,
        path: `/${operationIndex}`,
      },
    ];
  }
  if (createDashboardIndices[0] !== undefined && createDashboardIndices[0] !== 0) {
    const operationIndex = createDashboardIndices[0];
    return [
      {
        code: "invalid_operation_sequence",
        message: "create_dashboard must be the first operation",
        operationIndex,
        path: `/${operationIndex}`,
      },
    ];
  }

  return [];
}

function validateAgentRequest(value: unknown): AgentConfigError[] {
  if (validateRequest(value)) return [];
  return schemaErrors(validateRequest.errors || [], "invalid_request_schema");
}

function schemaErrors(errors: ErrorObject[], code: string): AgentConfigError[] {
  return errors.map((error) => ({
    code,
    message: error.message || "is invalid",
    operationIndex: operationIndexFromPath(error.instancePath),
    path: error.instancePath || "/",
    details: error.params,
  }));
}

function dashboardValidationErrors(
  failure: DashboardValidationFailure,
): AgentConfigError[] {
  return failure.errors.map((message, index) => ({
    code: "dashboard_validation_failed",
    message,
    path: failure.details[index]?.instancePath || "/",
    details: failure.details[index]?.params,
  }));
}

function operationIndexFromPath(path: string): number | undefined {
  const segments = path.split("/").filter(Boolean);
  const operationsIndex = segments.indexOf("operations");
  const candidate =
    operationsIndex >= 0 ? segments[operationsIndex + 1] : segments[0];
  const index = Number(candidate);
  return Number.isInteger(index) ? index : undefined;
}

type ApplyResult =
  | { ok: true; document: DashboardDocument }
  | { ok: false; errors: AgentConfigError[] };

function applyOperation(
  document: DashboardDocument,
  operation: AgentDashboardOperation,
  operationIndex: number,
): ApplyResult {
  const next = structuredClone(document);

  switch (operation.type) {
    case "create_dashboard":
      return { ok: true, document: structuredClone(operation.document) };
    case "add_section":
      return addSection(next, operation, operationIndex);
    case "add_service":
      return addService(next, operation, operationIndex);
    case "add_metric_card":
      return addMetricCard(next, operation, operationIndex);
    case "connect_datasource":
      return connectDatasource(next, operation, operationIndex);
    case "set_status_rule":
      return setStatusRule(next, operation, operationIndex);
    case "arrange_item":
      return arrangeItem(next, operation, operationIndex);
    case "remove_item":
      return removeItem(next, operation, operationIndex);
  }
}

type AddSectionOperation = Extract<AgentDashboardOperation, { type: "add_section" }>;
type AddServiceOperation = Extract<AgentDashboardOperation, { type: "add_service" }>;
type AddMetricCardOperation = Extract<AgentDashboardOperation, { type: "add_metric_card" }>;
type ConnectDatasourceOperation = Extract<AgentDashboardOperation, { type: "connect_datasource" }>;
type SetStatusRuleOperation = Extract<AgentDashboardOperation, { type: "set_status_rule" }>;
type ArrangeItemOperation = Extract<AgentDashboardOperation, { type: "arrange_item" }>;
type RemoveItemOperation = Extract<AgentDashboardOperation, { type: "remove_item" }>;

function addSection(
  document: DashboardDocument,
  operation: AddSectionOperation,
  operationIndex: number,
): ApplyResult {
  if (document.serviceGroups.some((group) => group.id === operation.section.id)) {
    return failure(
      "duplicate_id",
      `Service group already exists: ${operation.section.id}`,
      operationIndex,
      "/serviceGroups",
    );
  }

  const group: ServiceGroup = {
    ...operation.section,
    services: [],
  };
  document.serviceGroups.push(group);
  document.layout.serviceGroups = insertId(
    document.layout.serviceGroups,
    group.id,
    operation.position,
  );
  return { ok: true, document };
}

function addService(
  document: DashboardDocument,
  operation: AddServiceOperation,
  operationIndex: number,
): ApplyResult {
  const group = document.serviceGroups.find((item) => item.id === operation.groupId);
  if (!group) {
    return failure(
      "group_not_found",
      `Service group not found: ${operation.groupId}`,
      operationIndex,
      "/serviceGroups",
    );
  }

  if (
    document.serviceGroups.some((item) =>
      item.services.some((service) => service.id === operation.service.id),
    )
  ) {
    return failure(
      "duplicate_id",
      `Service already exists: ${operation.service.id}`,
      operationIndex,
      "/serviceGroups",
    );
  }

  group.services = insertItem(group.services, operation.service, operation.position);
  return { ok: true, document };
}

function addMetricCard(
  document: DashboardDocument,
  operation: AddMetricCardOperation,
  operationIndex: number,
): ApplyResult {
  if (document.telemetry.some((card) => card.id === operation.card.id)) {
    return failure(
      "duplicate_id",
      `Telemetry card already exists: ${operation.card.id}`,
      operationIndex,
      "/telemetry",
    );
  }

  document.telemetry.push(operation.card);
  document.layout.telemetry = insertId(
    document.layout.telemetry,
    operation.card.id,
    operation.position,
  );
  return { ok: true, document };
}

function connectDatasource(
  document: DashboardDocument,
  operation: ConnectDatasourceOperation,
  operationIndex: number,
): ApplyResult {
  if (!supportsDatasource(operation.target.kind)) {
    return failure(
      "unsupported_target",
      `Target does not support datasources: ${operation.target.kind}`,
      operationIndex,
      targetPath(operation.target),
    );
  }

  const targetResolution = resolveTarget(document, operation.target, operationIndex);
  if (!targetResolution.ok) {
    return { ok: false, errors: targetResolution.errors };
  }

  const target = targetResolution.target;
  (target as DatasourceAgentTarget).datasource = operation.datasource;
  return { ok: true, document };
}

function setStatusRule(
  document: DashboardDocument,
  operation: SetStatusRuleOperation,
  operationIndex: number,
): ApplyResult {
  if (operation.target.kind === "serviceGroup") {
    return failure(
      "unsupported_target",
      `Target does not support status rules: ${operation.target.kind}`,
      operationIndex,
      targetPath(operation.target),
    );
  }
  if (operation.thresholds && operation.target.kind !== "telemetry") {
    return failure(
      "unsupported_target",
      "Thresholds can only be set on telemetry targets",
      operationIndex,
      targetPath(operation.target),
    );
  }
  if (operation.detail !== undefined && !supportsDetail(operation.target.kind)) {
    return failure(
      "unsupported_target",
      "Detail can only be set on telemetry, service, or module targets",
      operationIndex,
      targetPath(operation.target),
    );
  }
  if (operation.value !== undefined && !supportsStringValue(operation.target.kind)) {
    return failure(
      "unsupported_target",
      "String values can only be set on status item or module targets",
      operationIndex,
      targetPath(operation.target),
    );
  }

  const targetResolution = resolveTarget(document, operation.target, operationIndex);
  if (!targetResolution.ok) {
    return { ok: false, errors: targetResolution.errors };
  }

  const target = targetResolution.target;
  if (operation.thresholds && operation.target.kind === "telemetry") {
    (target as TelemetryCard).thresholds = operation.thresholds;
  }
  if (operation.severity) {
    target.severity = operation.severity;
  }
  if (operation.detail !== undefined && supportsDetail(operation.target.kind)) {
    (target as DetailAgentTarget).detail = operation.detail;
  }
  if (operation.value !== undefined && supportsStringValue(operation.target.kind)) {
    (target as StringValueAgentTarget).value = operation.value;
  }

  return { ok: true, document };
}

function arrangeItem(
  document: DashboardDocument,
  operation: ArrangeItemOperation,
  operationIndex: number,
): ApplyResult {
  const list = layoutList(document, operation.area);
  if (!list.includes(operation.id)) {
    return failure(
      "item_not_found",
      `Layout item not found: ${operation.id}`,
      operationIndex,
      `/layout/${operation.area}`,
    );
  }

  const without = list.filter((id) => id !== operation.id);
  const index = Math.min(operation.index, without.length);
  without.splice(index, 0, operation.id);
  setLayoutList(document, operation.area, without);
  syncCollectionOrder(document, operation.area, without);
  return { ok: true, document };
}

function removeItem(
  document: DashboardDocument,
  operation: RemoveItemOperation,
  operationIndex: number,
): ApplyResult {
  if (operation.target.kind === "telemetry") {
    if (!document.telemetry.some((card) => card.id === operation.target.id)) {
      return failure(
        "item_not_found",
        `Telemetry card not found: ${operation.target.id}`,
        operationIndex,
        "/telemetry",
      );
    }
    document.telemetry = document.telemetry.filter((card) => card.id !== operation.target.id);
    document.layout.telemetry = document.layout.telemetry.filter((id) => id !== operation.target.id);
    return { ok: true, document };
  }

  if (operation.target.kind === "serviceGroup") {
    if (!document.serviceGroups.some((group) => group.id === operation.target.id)) {
      return failure(
        "item_not_found",
        `Service group not found: ${operation.target.id}`,
        operationIndex,
        "/serviceGroups",
      );
    }
    document.serviceGroups = document.serviceGroups.filter((group) => group.id !== operation.target.id);
    document.layout.serviceGroups = document.layout.serviceGroups.filter((id) => id !== operation.target.id);
    return { ok: true, document };
  }

  if (operation.target.kind === "service") {
    const matches = findServices(document, operation.target.id, operation.target.groupId);
    if (!matches.length) {
      return failure(
        "item_not_found",
        `Service not found: ${operation.target.id}`,
        operationIndex,
        "/serviceGroups",
      );
    }
    if (!operation.target.groupId && matches.length > 1) {
      return failure(
        "ambiguous_target",
        `Service exists in multiple groups: ${operation.target.id}`,
        operationIndex,
        "/serviceGroups",
      );
    }

    const match = matches[0];
    match.group.services = match.group.services.filter((service) => service.id !== operation.target.id);
    return { ok: true, document };
  }

  if (operation.target.kind === "module") {
    if (!(document.modules || []).some((module) => module.id === operation.target.id)) {
      return failure(
        "item_not_found",
        `Module not found: ${operation.target.id}`,
        operationIndex,
        "/modules",
      );
    }
    document.modules = (document.modules || []).filter((module) => module.id !== operation.target.id);
    document.layout.modules = (document.layout.modules || []).filter((id) => id !== operation.target.id);
    return { ok: true, document };
  }

  if (operation.target.kind === "statusItem") {
    const matches = findStatusItems(document, operation.target.id, operation.target.stripId);
    if (!matches.length) {
      return failure(
        "item_not_found",
        `Status item not found: ${operation.target.id}`,
        operationIndex,
        "/statusStrips",
      );
    }
    if (!operation.target.stripId && matches.length > 1) {
      return failure(
        "ambiguous_target",
        `Status item exists in multiple strips: ${operation.target.id}`,
        operationIndex,
        "/statusStrips",
      );
    }

    const [match] = matches;
    match.strip.items = match.strip.items.filter((item) => item.id !== operation.target.id);
    return { ok: true, document };
  }

  return failure("unsupported_target", "Cannot remove target kind", operationIndex, "/");
}

function resolveTarget(
  document: DashboardDocument,
  target: ConnectDatasourceOperation["target"],
  operationIndex: number,
): ResolveTargetResult {
  if (target.kind === "telemetry") {
    const card = document.telemetry.find((item) => item.id === target.id);
    if (!card) {
      return resolutionFailure(
        "item_not_found",
        `Telemetry card not found: ${target.id}`,
        operationIndex,
        "/telemetry",
      );
    }
    return { ok: true, target: card };
  }
  if (target.kind === "service") {
    const matches = findServices(document, target.id, target.groupId);
    if (!matches.length) {
      return resolutionFailure(
        "item_not_found",
        `Service not found: ${target.id}`,
        operationIndex,
        "/serviceGroups",
      );
    }
    if (!target.groupId && matches.length > 1) {
      return resolutionFailure(
        "ambiguous_target",
        `Service exists in multiple groups: ${target.id}`,
        operationIndex,
        "/serviceGroups",
      );
    }
    return { ok: true, target: matches[0].service };
  }
  if (target.kind === "statusItem") {
    const matches = findStatusItems(document, target.id, target.stripId);
    if (!matches.length) {
      return resolutionFailure(
        "item_not_found",
        `Status item not found: ${target.id}`,
        operationIndex,
        "/statusStrips",
      );
    }
    if (!target.stripId && matches.length > 1) {
      return resolutionFailure(
        "ambiguous_target",
        `Status item exists in multiple strips: ${target.id}`,
        operationIndex,
        "/statusStrips",
      );
    }
    return { ok: true, target: matches[0].item };
  }
  if (target.kind === "module") {
    const module = (document.modules || []).find((item) => item.id === target.id);
    if (!module) {
      return resolutionFailure(
        "item_not_found",
        `Module not found: ${target.id}`,
        operationIndex,
        "/modules",
      );
    }
    return { ok: true, target: module };
  }
  return resolutionFailure(
    "item_not_found",
    `Target not found: ${target.id}`,
    operationIndex,
    targetPath(target),
  );
}

type ResolveTargetResult =
  | { ok: true; target: ResolvedAgentTarget }
  | { ok: false; errors: AgentConfigError[] };
type ResolvedAgentTarget = TelemetryCard | ServiceEntry | StatusItem | DashboardModule;
type DatasourceAgentTarget = TelemetryCard | ServiceEntry | DashboardModule;
type DetailAgentTarget = TelemetryCard | ServiceEntry | DashboardModule;
type StringValueAgentTarget = StatusItem | DashboardModule;

type DatasourceTargetKind = Extract<
  ConnectDatasourceOperation["target"]["kind"],
  "telemetry" | "service" | "module"
>;

function supportsDatasource(
  kind: ConnectDatasourceOperation["target"]["kind"],
): kind is DatasourceTargetKind {
  return kind === "telemetry" || kind === "service" || kind === "module";
}

type DetailTargetKind = Extract<
  SetStatusRuleOperation["target"]["kind"],
  "telemetry" | "service" | "module"
>;

function supportsDetail(
  kind: SetStatusRuleOperation["target"]["kind"],
): kind is DetailTargetKind {
  return kind === "telemetry" || kind === "service" || kind === "module";
}

type StringValueTargetKind = Extract<
  SetStatusRuleOperation["target"]["kind"],
  "statusItem" | "module"
>;

function supportsStringValue(
  kind: SetStatusRuleOperation["target"]["kind"],
): kind is StringValueTargetKind {
  return kind === "statusItem" || kind === "module";
}

function findServices(document: DashboardDocument, id: string, groupId?: string) {
  const groups = groupId
    ? document.serviceGroups.filter((group) => group.id === groupId)
    : document.serviceGroups;
  return groups.flatMap((group) =>
    group.services.flatMap((service) =>
      service.id === id ? [{ group, service }] : [],
    ),
  );
}

function findStatusItems(document: DashboardDocument, id: string, stripId?: string) {
  const strips = stripId
    ? document.statusStrips.filter((strip) => strip.id === stripId)
    : document.statusStrips;
  return strips.flatMap((strip) =>
    strip.items.flatMap((item) => (item.id === id ? [{ strip, item }] : [])),
  );
}

function resolutionFailure(
  code: string,
  message: string,
  operationIndex: number,
  path: string,
): ResolveTargetResult {
  return {
    ok: false,
    errors: [
      {
        code,
        message,
        operationIndex,
        path,
      },
    ],
  };
}

function targetPath(target: ConnectDatasourceOperation["target"]): string {
  if (target.kind === "telemetry") return "/telemetry";
  if (target.kind === "service") return "/serviceGroups";
  if (target.kind === "serviceGroup") return "/serviceGroups";
  if (target.kind === "statusItem") return "/statusStrips";
  return "/modules";
}

function layoutList(document: DashboardDocument, area: ArrangeItemOperation["area"]) {
  if (area === "modules") return document.layout.modules || [];
  return document.layout[area];
}

function setLayoutList(
  document: DashboardDocument,
  area: ArrangeItemOperation["area"],
  value: string[],
) {
  if (area === "modules") {
    document.layout.modules = value;
    return;
  }
  document.layout[area] = value;
}

function syncCollectionOrder(
  document: DashboardDocument,
  area: ArrangeItemOperation["area"],
  orderedIds: string[],
) {
  if (area === "telemetry") {
    document.telemetry = orderByIds(document.telemetry, orderedIds);
    return;
  }
  if (area === "serviceGroups") {
    document.serviceGroups = orderByIds(document.serviceGroups, orderedIds);
    return;
  }
  if (area === "statusStrips") {
    document.statusStrips = orderByIds(document.statusStrips, orderedIds);
    return;
  }
  document.modules = orderByIds(document.modules || [], orderedIds);
}

function orderByIds<T extends { id: string }>(items: T[], orderedIds: string[]): T[] {
  const order = new Map(orderedIds.map((id, index) => [id, index]));
  return [...items].sort(
    (left, right) =>
      (order.get(left.id) ?? Number.MAX_SAFE_INTEGER) -
      (order.get(right.id) ?? Number.MAX_SAFE_INTEGER),
  );
}

function insertItem<T extends { id: string }>(
  items: T[],
  item: T,
  position?: Static<typeof PositionSchema>,
): T[] {
  const next = [...items];
  next.splice(resolveInsertionIndex(next.map((entry) => entry.id), position), 0, item);
  return next;
}

function insertId(
  ids: string[],
  id: string,
  position?: Static<typeof PositionSchema>,
): string[] {
  const next = [...ids];
  next.splice(resolveInsertionIndex(next, position), 0, id);
  return next;
}

function resolveInsertionIndex(ids: string[], position?: Static<typeof PositionSchema>) {
  if (!position) return ids.length;
  if (typeof position.index === "number") return Math.min(position.index, ids.length);
  if (position.beforeId) {
    const beforeIndex = ids.indexOf(position.beforeId);
    return beforeIndex >= 0 ? beforeIndex : ids.length;
  }
  if (position.afterId) {
    const afterIndex = ids.indexOf(position.afterId);
    return afterIndex >= 0 ? afterIndex + 1 : ids.length;
  }
  return ids.length;
}

function failure(
  code: string,
  message: string,
  operationIndex: number,
  path: string,
): ApplyResult {
  return {
    ok: false,
    errors: [
      {
        code,
        message,
        operationIndex,
        path,
      },
    ],
  };
}

function createJsonPatch(
  before: unknown,
  after: unknown,
  path = "",
): JsonPatchOperation[] {
  if (before === undefined) return [{ op: "add", path: path || "/", value: after }];
  if (after === undefined) return [{ op: "remove", path: path || "/" }];
  if (Object.is(before, after)) return [];

  if (!isObjectLike(before) || !isObjectLike(after)) {
    return [{ op: "replace", path: path || "/", value: after }];
  }

  if (Array.isArray(before) || Array.isArray(after)) {
    if (!Array.isArray(before) || !Array.isArray(after)) {
      return [{ op: "replace", path: path || "/", value: after }];
    }
    const operations: JsonPatchOperation[] = [];
    const sharedLength = Math.min(before.length, after.length);
    for (let index = 0; index < sharedLength; index += 1) {
      operations.push(
        ...createJsonPatch(before[index], after[index], `${path}/${index}`),
      );
    }
    for (let index = before.length - 1; index >= after.length; index -= 1) {
      operations.push({ op: "remove", path: `${path}/${index}` });
    }
    for (let index = before.length; index < after.length; index += 1) {
      operations.push({ op: "add", path: `${path}/${index}`, value: after[index] });
    }
    return operations;
  }

  const beforeObject = before as Record<string, unknown>;
  const afterObject = after as Record<string, unknown>;
  const keys = new Set([...Object.keys(beforeObject), ...Object.keys(afterObject)]);
  return [...keys].flatMap((key) =>
    createJsonPatch(beforeObject[key], afterObject[key], `${path}/${escapeJsonPointer(key)}`),
  );
}

function isObjectLike(value: unknown): value is Record<string, unknown> | unknown[] {
  return typeof value === "object" && value !== null;
}

function escapeJsonPointer(value: string): string {
  return value.replace(/~/g, "~0").replace(/\//g, "~1");
}

async function parseRequestBody(request: Request): Promise<unknown> {
  const text = await request.text();
  return text ? JSON.parse(text) : {};
}

function previewBaseDocument(operations: AgentDashboardOperation[]): DashboardDocument | null {
  return operations[0]?.type === "create_dashboard" ? emptyDashboardDocument() : null;
}

function serializeRevision(revision: DashboardRevision) {
  return {
    id: revision.id,
    dashboardId: revision.dashboardId,
    schemaVersion: revision.schemaVersion,
    actor: revision.actor,
    message: revision.message,
    operation: revision.operation,
    sourceRevisionId: revision.sourceRevisionId,
    createdAt: revision.createdAt.toISOString(),
  };
}

function emptyDashboardDocument(): DashboardDocument {
  return {
    schemaVersion: "dashboard.v1",
    metadata: {
      title: "Untitled Dashboard",
    },
    layout: {
      telemetry: [],
      serviceGroups: [],
      statusStrips: [],
      modules: [],
    },
    telemetry: [],
    serviceGroups: [],
    statusStrips: [],
    modules: [],
  };
}
