import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { after, before, test } from "node:test";

import { createServer } from "vite";

let server;
let parseDevicePluginManifest;
let createDevicePluginRegistry;
let xgridsK1Manifest;
let xgridsK1Actions;
let xgridsK1Api;
let ApiError;
let localizeRuntimeMessage;
let lifecycle;
let projectName;
let automaticSourceStart;
let presentation;
let operatorIntentGeneration;
let configuration;
let compatibility;

before(async () => {
  server = await createServer({
    appType: "custom",
    logLevel: "silent",
    server: { middlewareMode: true },
  });
  ({ parseDevicePluginManifest } = await server.ssrLoadModule(
    "/src/core/device-plugins/manifestParser.ts",
  ));
  ({ createDevicePluginRegistry } = await server.ssrLoadModule(
    "/src/core/device-plugins/registry.ts",
  ));
  ({ xgridsK1Manifest, xgridsK1Actions } = await server.ssrLoadModule(
    "@xgrids-k1/frontend/manifest.ts",
  ));
  lifecycle = await server.ssrLoadModule(
    "@xgrids-k1/frontend/lifecycle.ts",
  );
  projectName = await server.ssrLoadModule(
    "@xgrids-k1/frontend/projectName.ts",
  );
  automaticSourceStart = await server.ssrLoadModule(
    "@xgrids-k1/frontend/automaticSourceStart.ts",
  );
  presentation = await server.ssrLoadModule(
    "@xgrids-k1/frontend/presentation.ts",
  );
  operatorIntentGeneration = await server.ssrLoadModule(
    "@xgrids-k1/frontend/operatorIntentGeneration.ts",
  );
  configuration = await server.ssrLoadModule(
    "@xgrids-k1/frontend/configuration.ts",
  );
  compatibility = await server.ssrLoadModule(
    "@xgrids-k1/frontend/compatibility.ts",
  );
  ({ xgridsK1Api, ApiError } = await server.ssrLoadModule(
    "@xgrids-k1/frontend/api.ts",
  ));
  ({ localizeRuntimeMessage } = await server.ssrLoadModule(
    "@xgrids-k1/frontend/messages.ts",
  ));
});

after(async () => {
  await server?.close();
});

function manifestDocument({
  apiVersion = "missioncore.nodedc/v1alpha1",
  pluginId = "test.device.plugin",
  modelId = "test.device.model",
} = {}) {
  const v1alpha2 = apiVersion === "missioncore.nodedc/v1alpha2";
  return {
    apiVersion,
    kind: "DevicePlugin",
    metadata: {
      id: pluginId,
      version: "1.0.0",
      displayName: "Test device",
    },
    spec: {
      hostApiRange: v1alpha2 ? "v1alpha2" : "v1alpha1",
      runtime: {
        backendEntrypoint: "test.plugin:build",
        isolation: "transitional-in-process",
      },
      ...(v1alpha2
        ? {
            compatibilityProfiles: [
              {
                profileId: `${modelId}.fw-1.v1`,
                path: "profiles/fw-1/profile.v1.json",
                modelId,
              },
            ],
          }
        : {}),
      permissions: ["device.read"],
      actions: [{ id: "state.read", mutating: false, secretFields: [] }],
      models: [
        {
          id: modelId,
          vendor: "Test",
          displayName: "Test model",
          category: "Sensor",
          description: "Fixture model",
          verified: true,
          capabilities: [{ id: "device.read", label: "Read" }],
          ui: { slot: "device.connection", componentKey: "test.connection" },
        },
      ],
    },
  };
}

function uiPlugin(manifest) {
  return {
    manifest,
    RuntimeProvider: ({ children }) => children,
    connectionViews: { "test.connection": () => null },
  };
}

test("parser accepts reviewed v1alpha1 and v1alpha2 shapes", () => {
  const legacy = parseDevicePluginManifest(manifestDocument());
  const current = parseDevicePluginManifest(
    manifestDocument({ apiVersion: "missioncore.nodedc/v1alpha2" }),
  );

  assert.equal(legacy.apiVersion, "missioncore.nodedc/v1alpha1");
  assert.equal(legacy.spec.hostApiRange, "v1alpha1");
  assert.equal(current.apiVersion, "missioncore.nodedc/v1alpha2");
  assert.equal(current.spec.hostApiRange, "v1alpha2");
  assert.deepEqual(current.spec.compatibilityProfiles, [
    {
      profileId: "test.device.model.fw-1.v1",
      path: "profiles/fw-1/profile.v1.json",
      modelId: "test.device.model",
    },
  ]);
});

test("v1alpha1 keeps the original nonblank identifier compatibility", () => {
  const legacyDocument = manifestDocument({
    pluginId: "legacy plugin id",
    modelId: "legacy model id",
  });
  legacyDocument.spec.permissions = ["legacy permission"];
  legacyDocument.spec.actions[0].secretFields = ["legacy secret field"];
  legacyDocument.spec.models[0].capabilities = [
    { id: "legacy capability", label: "Legacy" },
  ];

  const legacy = parseDevicePluginManifest(legacyDocument);

  assert.equal(legacy.metadata.id, "legacy plugin id");
  assert.equal(legacy.spec.models[0].id, "legacy model id");
  assert.deepEqual(legacy.spec.permissions, ["legacy permission"]);
});

test("v1alpha2 applies strict identifiers without redefining v1alpha1", () => {
  const current = manifestDocument({
    apiVersion: "missioncore.nodedc/v1alpha2",
    pluginId: "current plugin id",
  });

  assert.throws(
    () => parseDevicePluginManifest(current),
    /не является идентификатором/,
  );
});

test("v1alpha2 parser and registry accept multiple independently profiled models", () => {
  const document = manifestDocument({
    apiVersion: "missioncore.nodedc/v1alpha2",
    pluginId: "test.family",
    modelId: "test.family.model-a",
  });
  document.spec.models.push({
    ...document.spec.models[0],
    id: "test.family.model-b",
    displayName: "Test model B",
  });
  document.spec.compatibilityProfiles.push({
    profileId: "test.family.model-b.fw-1.v1",
    path: "profiles/fw-1/model-b.v1.json",
    modelId: "test.family.model-b",
  });

  const manifest = parseDevicePluginManifest(document);
  const plugin = {
    ...uiPlugin(manifest),
    connectionViews: { "test.connection": () => null },
  };
  const registry = createDevicePluginRegistry([plugin]);

  assert.deepEqual(registry.models.map(({ model }) => model.id), [
    "test.family.model-a",
    "test.family.model-b",
  ]);
});

test("installed XGRIDS frontend manifest exposes the semantic v1alpha2 actions", () => {
  assert.equal(xgridsK1Manifest.apiVersion, "missioncore.nodedc/v1alpha2");
  assert.deepEqual(xgridsK1Manifest.spec.compatibilityProfiles, [
    {
      profileId: "xgrids.lixelkity-k1.fw-3.0.2.local-network.v2",
      path: "profiles/fw-3.0.2/local-network.v2.json",
      modelId: "xgrids.lixelkity-k1",
    },
  ]);
  assert.equal(xgridsK1Actions.acquisitionPrepare, "acquisition.prepare");
  assert.equal(xgridsK1Actions.acquisitionStart, "acquisition.start");
  assert.equal(xgridsK1Actions.acquisitionStop, "acquisition.stop");
});

test("one operator action can open at most one K1 control session", () => {
  assert.equal(lifecycle.controlSessionEntryPlan("idle", false, true), "open");
  assert.equal(lifecycle.controlSessionEntryPlan("failed", false, true), "open");
  assert.equal(lifecycle.controlSessionEntryPlan("failed", false, false), "failed");
  assert.equal(lifecycle.controlSessionEntryPlan("failed", true, true), "failed");
  assert.equal(
    lifecycle.controlSessionEntryPlan("idle", true, true),
    "duplicate-open",
  );
  assert.equal(
    lifecycle.controlSessionEntryPlan("connection-ready", true, false),
    "continue",
  );
});

test("K1 operator intent stays invalid after runtime deactivate and reactivate", () => {
  const generation = new operatorIntentGeneration.OperatorIntentGeneration();
  generation.activateRuntime();
  const oldIntent = generation.beginOperatorIntent();

  assert.ok(oldIntent);
  assert.equal(generation.isOperatorIntentCurrent(oldIntent), true);

  generation.deactivateRuntime();
  generation.activateRuntime();
  const freshIntent = generation.beginOperatorIntent();

  assert.ok(freshIntent);
  assert.notEqual(freshIntent.runtimeGeneration, oldIntent.runtimeGeneration);
  assert.equal(generation.isOperatorIntentCurrent(oldIntent), false);
  assert.equal(generation.isOperatorIntentCurrent(freshIntent), true);
});

test("stale K1 intent cannot continue past an await after runtime reactivation", async () => {
  const generation = new operatorIntentGeneration.OperatorIntentGeneration();
  generation.activateRuntime();
  const oldIntent = generation.beginOperatorIntent();
  assert.ok(oldIntent);

  let resolveOldRead;
  const oldRead = new Promise((resolve) => {
    resolveOldRead = resolve;
  });
  const writes = [];
  const assertOldIntent = () => {
    if (!generation.isOperatorIntentCurrent(oldIntent)) {
      throw new Error("stale operator intent");
    }
  };
  const oldContinuation = operatorIntentGeneration.awaitWhileIntentCurrent(
    assertOldIntent,
    () => oldRead,
  ).then(() => writes.push("old-checkpoint"));

  generation.deactivateRuntime();
  generation.activateRuntime();
  const freshIntent = generation.beginOperatorIntent();
  assert.ok(freshIntent);
  resolveOldRead({ phase: "connection-ready" });

  await assert.rejects(oldContinuation, /stale operator intent/);
  assert.deepEqual(writes, []);

  const assertFreshIntent = () => {
    if (!generation.isOperatorIntentCurrent(freshIntent)) {
      throw new Error("stale fresh intent");
    }
  };
  await operatorIntentGeneration.awaitWhileIntentCurrent(
    assertFreshIntent,
    async () => ({ phase: "connection-ready" }),
  );
  writes.push("fresh-checkpoint");
  assert.deepEqual(writes, ["fresh-checkpoint"]);
});

test("a fresh explicit K1 intent supersedes the previous intent in one runtime", () => {
  const generation = new operatorIntentGeneration.OperatorIntentGeneration();
  generation.activateRuntime();
  const first = generation.beginOperatorIntent();
  const second = generation.beginOperatorIntent();

  assert.ok(first);
  assert.ok(second);
  assert.equal(first.runtimeGeneration, second.runtimeGeneration);
  assert.notEqual(first.intentGeneration, second.intentGeneration);
  assert.equal(generation.isOperatorIntentCurrent(first), false);
  assert.equal(generation.isOperatorIntentCurrent(second), true);
});

test("canonical K1 launch wires the generation guard through every async stage", async () => {
  const hookSource = await readFile(
    new URL(
      "../../../plugins/xgrids-k1/frontend/src/useXgridsK1Runtime.ts",
      import.meta.url,
    ),
    "utf8",
  );
  const canonicalStart = hookSource.slice(
    hookSource.indexOf("const startCanonicalAcquisition"),
    hookSource.indexOf("const prepareAcquisition"),
  );
  const pollingLoop = hookSource.slice(
    hookSource.indexOf("async function waitForControlPhase"),
    hookSource.indexOf("function messageFor"),
  );

  assert.doesNotMatch(hookSource, /mounted\.current/);
  assert.match(canonicalStart, /beginOperatorIntent\(\)/);
  assert.match(canonicalStart, /isOperatorIntentCurrent\(intentToken\)/);
  assert.doesNotMatch(canonicalStart, /await xgridsK1Api\./);
  assert.doesNotMatch(canonicalStart, /await waitForControlPhase\(/);
  assert.ok(
    canonicalStart.match(/awaitWhileIntentCurrent\(/g)?.length >= 8,
    "each canonical REST/checkpoint boundary must use the intent guard",
  );
  assert.match(pollingLoop, /for \(;;\) \{\s*assertOperatorIntentCurrent\(\)/);
  assert.equal(pollingLoop.match(/awaitWhileIntentCurrent\(/g)?.length, 2);
});

test("K1 control errors stay informative and only fetch failures mark transport unavailable", async () => {
  assert.equal(
    localizeRuntimeMessage(
      "control MQTT connect call failed: [Errno 61] Connection refused",
    ),
    "Управляющее соединение со сканером не открылось: устройство не приняло MQTT-соединение. Команды сканирования не отправлялись.",
  );

  const domainError = new ApiError("Диалог остановлен до команды START.");
  assert.equal(domainError.transportUnavailable, false);

  const originalFetch = globalThis.fetch;
  globalThis.fetch = async () => {
    throw new TypeError("synthetic network failure");
  };
  try {
    await assert.rejects(
      () => xgridsK1Api.getState(),
      (error) => error instanceof ApiError && error.transportUnavailable === true,
    );
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("v1alpha2 compatibility profile is exact-key and rejects duplicated profile status", () => {
  const document = manifestDocument({ apiVersion: "missioncore.nodedc/v1alpha2" });
  document.spec.compatibilityProfiles[0].status = "verified";

  assert.throws(
    () => parseDevicePluginManifest(document),
    /неизвестные поля status/,
  );
});

test("v1alpha2 compatibility profile fails closed on unsafe paths", () => {
  const document = manifestDocument({ apiVersion: "missioncore.nodedc/v1alpha2" });
  document.spec.compatibilityProfiles[0].path = "../private/profile.json";

  assert.throws(
    () => parseDevicePluginManifest(document),
    /безопасным относительным JSON-путём/,
  );
});

test("v1alpha2 compatibility profile cannot reference an unknown model", () => {
  const document = manifestDocument({ apiVersion: "missioncore.nodedc/v1alpha2" });
  document.spec.compatibilityProfiles[0].modelId = "missing.model";

  assert.throws(
    () => parseDevicePluginManifest(document),
    /ссылается на неизвестную модель missing\.model/,
  );
});

test("registry accepts v1alpha1 and v1alpha2 plugins together", () => {
  const legacy = parseDevicePluginManifest(
    manifestDocument({ pluginId: "test.legacy", modelId: "test.legacy.model" }),
  );
  const current = parseDevicePluginManifest(
    manifestDocument({
      apiVersion: "missioncore.nodedc/v1alpha2",
      pluginId: "test.current",
      modelId: "test.current.model",
    }),
  );
  const registry = createDevicePluginRegistry([uiPlugin(legacy), uiPlugin(current)]);

  assert.equal(registry.plugins.length, 2);
  assert.equal(registry.models.length, 2);
  assert.equal(registry.resolveModel("test.current.model")?.plugin.manifest.metadata.id, "test.current");
});

test("registry independently rejects an uncovered v1alpha2 model", () => {
  const current = parseDevicePluginManifest(
    manifestDocument({ apiVersion: "missioncore.nodedc/v1alpha2" }),
  );
  current.spec.compatibilityProfiles[0].modelId = "missing.model";

  assert.throws(
    () => createDevicePluginRegistry([uiPlugin(current)]),
    /ссылается на неизвестную модель missing\.model/,
  );
});

test("live source is confirmed only by an acquiring acquisition", () => {
  const waiting = {
    source_mode: "live",
    phase: "live",
    acquisition: { state: "awaiting_external_start" },
  };
  const acquiring = {
    ...waiting,
    acquisition: { state: "acquiring" },
  };

  assert.equal(lifecycle.isConfirmedLiveState(waiting), false);
  assert.equal(lifecycle.confirmedRuntimeSourceMode(waiting), "idle");
  assert.equal(lifecycle.sourceStatusLabel(waiting), "Ожидание реальных данных");
  assert.equal(lifecycle.isConfirmedLiveState(acquiring), true);
  assert.equal(lifecycle.confirmedRuntimeSourceMode(acquiring), "live");
});

test("prepared acquisition resumes without another prepare and remains recoverable", () => {
  const prepared = {
    source_mode: "idle",
    acquisition: {
      acquisition_id: "acq-1",
      state: "prepared",
    },
  };

  assert.equal(lifecycle.liveStartPlan(prepared), "resume-prepared");
  assert.equal(lifecycle.recoverableAcquisition(prepared)?.acquisition_id, "acq-1");
});

test("project name is canonicalized and rejected outside the bounded safe contract", () => {
  assert.deepEqual(projectName.validateProjectName("  Ｍｉｓｓｉｏｎ　01  "), {
    value: "Mission 01",
    error: null,
  });
  assert.match(projectName.validateProjectName("line\nbreak").error, /управляющие/);
  assert.match(projectName.validateProjectName("\ud800").error, /управляющие/);
  assert.match(projectName.validateProjectName("x".repeat(97)).error, /не длиннее 96/);
  assert.match(projectName.validateProjectName(" \t ").error, /Введите название/);
});

test("automatic spatial source replaces the old scene only after a successful start", async () => {
  const failedEvents = [];
  assert.equal(await automaticSourceStart.runAutomaticSpatialSourceStart(
    async () => {
      failedEvents.push("start");
      return false;
    },
    () => failedEvents.push("activate"),
    () => failedEvents.push("open"),
  ), false);
  assert.deepEqual(failedEvents, ["start"]);

  const successfulEvents = [];
  assert.equal(await automaticSourceStart.runAutomaticSpatialSourceStart(
    async () => {
      successfulEvents.push("start");
      return true;
    },
    () => successfulEvents.push("activate"),
    () => successfulEvents.push("open"),
  ), true);
  assert.deepEqual(successfulEvents, ["start", "activate", "open"]);
});

test("vendor commands fail closed unless the profile and acquisition both enable them", () => {
  const capability = {
    compatibility: {
      vendor_writes_enabled: true,
      permitted_mode: "active-control",
    },
  };
  assert.equal(lifecycle.isVendorWriteCapable(capability), true);
  assert.equal(lifecycle.isVendorWriteCapable({
    compatibility: { vendor_writes_enabled: true, permitted_mode: "read-only" },
  }), false);
  assert.equal(lifecycle.isSoftwareCommandedAcquisition({
    ...capability,
    acquisition: { control_mode: "operator-manual" },
  }), false);
  assert.equal(lifecycle.isSoftwareCommandedAcquisition({
    ...capability,
    acquisition: { control_mode: "plugin-commanded" },
  }), true);
});

test("device modeling telemetry maps only finite non-negative values", () => {
  assert.deepEqual(presentation.deviceTelemetry({
    device_elapsed_seconds: 12.5,
    device_route_distance_meters: 8.25,
    device_speed_meters_per_second: 0.75,
  }), {
    elapsedSeconds: 12.5,
    routeDistanceMeters: 8.25,
    speedMetersPerSecond: 0.75,
  });
  assert.deepEqual(presentation.deviceTelemetry({
    device_elapsed_seconds: -1,
    device_route_distance_meters: Number.NaN,
    device_speed_meters_per_second: Number.POSITIVE_INFINITY,
  }), {
    elapsedSeconds: null,
    routeDistanceMeters: null,
    speedMetersPerSecond: null,
  });
});

test("spatial K1 action failures have an explicit retry-safe presentation", () => {
  assert.equal(presentation.spatialActionFailure(null), null);
  assert.equal(presentation.spatialActionFailure("   "), null);
  assert.deepEqual(presentation.spatialActionFailure(" stop failed "), {
    title: "Действие K1 не выполнено",
    detail: "stop failed",
  });
  assert.equal(lifecycle.shouldRenderSpatialControls({
    source_mode: "live",
    acquisition: { state: "failed", cleanup_pending: true },
  }), true);
  assert.equal(lifecycle.shouldRenderSpatialControls({
    source_mode: "idle",
    acquisition: { state: "failed", cleanup_pending: false },
  }), false);
});

test("replay ignores a stale failed live acquisition", () => {
  const replay = {
    source_mode: "replay",
    phase: "replay",
    rerun_grpc_url: "rerun+http://127.0.0.1:9876/proxy",
    acquisition: {
      acquisition_id: "old-live-acquisition",
      state: "failed",
    },
  };

  assert.equal(lifecycle.normalizeRuntimePhase(replay), "replaying");
  assert.equal(lifecycle.effectiveAcquisition(replay), null);
  assert.equal(
    lifecycle.spatialSourceId(replay, replay.rerun_grpc_url),
    `replay:${replay.rerun_grpc_url}`,
  );
});

test("K1 configuration exposes all reviewed local connection directions", () => {
  assert.equal(configuration.DEFAULT_CONNECTION_MODE, "bridge");
  assert.equal(configuration.SUPPORTED_MOUNT_TYPE, "handheld");
  assert.equal(configuration.SUPPORTED_GNSS_MODE, "none");
  assert.deepEqual(
    configuration.connectionModeOptions.map(({ value, disabled = false }) => ({ value, disabled })),
    [
      { value: "bridge", disabled: false },
      { value: "quick-connect", disabled: false },
      { value: "direct-connect", disabled: false },
    ],
  );
  assert.deepEqual(
    configuration.mountTypeOptions.map(({ value, disabled = false }) => ({ value, disabled })),
    [
      { value: "handheld", disabled: false },
      { value: "vehicle-mounted", disabled: true },
      { value: "uav", disabled: true },
      { value: "backpack", disabled: true },
    ],
  );
  assert.deepEqual(
    configuration.gnssModeOptions.map(({ value, disabled = false }) => ({ value, disabled })),
    [
      { value: "none", disabled: false },
      { value: "rtk", disabled: true },
      { value: "ppk", disabled: true },
    ],
  );
});

test("connection directions select distinct fail-closed topologies", () => {
  assert.deepEqual(compatibility.profileSelectionForConnectionMode("bridge"), {
    firmware_version: "3.0.2",
    topology: "direct-lan",
    verification: "live-device-info",
  });
  assert.equal(
    compatibility.profileSelectionForConnectionMode("quick-connect").topology,
    "device-ap",
  );
  assert.equal(
    compatibility.profileSelectionForConnectionMode("direct-connect").topology,
    "controller-hotspot",
  );
});

test("a provisioned address is not presented as a verified device connection", () => {
  assert.equal(lifecycle.normalizeRuntimePhase({
    phase: "connected",
    application_control_session: { state: "idle" },
  }), "configuring");
  assert.equal(lifecycle.normalizeRuntimePhase({
    phase: "connected",
    application_control_session: { state: "connection-ready" },
  }), "connected");
});

test("provisioning intent keeps one idempotency key and exposes unsafe outcomes", () => {
  let created = 0;
  const createUuid = () => {
    created += 1;
    return "11111111-1111-4111-8111-111111111111";
  };
  const first = lifecycle.provisioningIntentKey(null, createUuid);
  const repeated = lifecycle.provisioningIntentKey(first, createUuid);
  const failedOperation = {
    status: "failed",
    error: { safe_to_retry: false },
  };

  assert.equal(first, "network-provision:11111111-1111-4111-8111-111111111111");
  assert.equal(repeated, first);
  assert.equal(created, 1);
  assert.equal(lifecycle.operationNeedsReconciliation(failedOperation), true);
});

test("device mutations send explicit nested compatibility attestation", async () => {
  const originalFetch = globalThis.fetch;
  const calls = [];
  const syntheticCredential = "x".repeat(32);
  globalThis.fetch = async (path, init) => {
    calls.push({ path, init });
    return new Response(JSON.stringify({ state: { source_mode: "idle" } }), {
      status: 200,
      headers: { "Content-Type": "application/json" },
    });
  };
  const attestation = {
    firmware_version: "3.0.2",
    topology: "direct-lan",
    verification: "live-device-info",
  };

  try {
    await xgridsK1Api.connect({
      device_id: "ble-device",
      ssid: "lab-network",
      password: syntheticCredential,
      connection_mode: "bridge",
      compatibility_attestation: attestation,
      idempotency_key: "network-provision:test",
    });
    await xgridsK1Api.prepareAcquisition({
      project_name: "Mission 01",
      mount_type: "handheld",
      gnss_mode: "none",
      compatibility_attestation: attestation,
    });
  } finally {
    globalThis.fetch = originalFetch;
  }

  assert.equal(calls.length, 2);
  const provisioning = JSON.parse(calls[0].init.body);
  const prepare = JSON.parse(calls[1].init.body);
  assert.deepEqual(provisioning.input.compatibility_attestation, attestation);
  assert.equal(provisioning.input.idempotency_key, "network-provision:test");
  assert.deepEqual(prepare.input.compatibility_attestation, attestation);
  assert.equal(prepare.input.project_name, "Mission 01");
});
