import {
  DEVICE_PLUGIN_API_VERSION_V1ALPHA1,
  DEVICE_STATE_READ_ACTION_ID,
  SUPPORTED_DEVICE_PLUGIN_API_VERSIONS,
  isDevicePluginManifestV1Alpha2,
  type DeviceUiPlugin,
  type RegisteredDeviceModel,
} from "./contracts";

export interface DevicePluginRegistry {
  readonly plugins: readonly DeviceUiPlugin[];
  readonly models: readonly RegisteredDeviceModel[];
  resolveModel: (modelId: string) => RegisteredDeviceModel | null;
}

export function createDevicePluginRegistry(
  installedPlugins: readonly DeviceUiPlugin[],
): DevicePluginRegistry {
  const pluginIds = new Set<string>();
  const modelIds = new Set<string>();
  const models: RegisteredDeviceModel[] = [];

  for (const plugin of installedPlugins) {
    const { manifest } = plugin;
    if (!SUPPORTED_DEVICE_PLUGIN_API_VERSIONS.includes(manifest.apiVersion)) {
      throw new Error(
        `Плагин ${manifest.metadata.id} использует несовместимый контракт ${manifest.apiVersion}.`,
      );
    }
    if (manifest.kind !== "DevicePlugin") {
      throw new Error(`Неподдерживаемый kind плагина: ${manifest.kind}.`);
    }
    if (!manifest.metadata.id.trim() || pluginIds.has(manifest.metadata.id)) {
      throw new Error(
        `Идентификатор плагина пуст или повторяется: ${manifest.metadata.id || "<empty>"}.`,
      );
    }
    pluginIds.add(manifest.metadata.id);

    if (
      manifest.apiVersion === DEVICE_PLUGIN_API_VERSION_V1ALPHA1 &&
      manifest.spec.models.length !== 1
    ) {
      throw new Error(
        `Плагин ${manifest.metadata.id} должен объявлять ровно одну модель в v1alpha1.`,
      );
    }
    if (!manifest.spec.models.length) {
      throw new Error(`Плагин ${manifest.metadata.id} не объявляет ни одной модели.`);
    }

    const permissions = new Set(manifest.spec.permissions);
    if (permissions.size !== manifest.spec.permissions.length) {
      throw new Error(`Плагин ${manifest.metadata.id} повторяет permission.`);
    }

    const actionIds = new Set<string>();
    for (const action of manifest.spec.actions) {
      if (!action.id.trim() || actionIds.has(action.id)) {
        throw new Error(
          `Идентификатор действия пуст или повторяется: ${action.id || "<empty>"}.`,
        );
      }
      actionIds.add(action.id);
      if (new Set(action.secretFields).size !== action.secretFields.length) {
        throw new Error(`Действие ${action.id} повторяет secret field.`);
      }
    }
    const stateRead = manifest.spec.actions.find(
      (action) => action.id === DEVICE_STATE_READ_ACTION_ID,
    );
    if (!stateRead || stateRead.mutating || stateRead.secretFields.length) {
      throw new Error(
        `Плагин ${manifest.metadata.id} должен объявлять безопасное действие state.read.`,
      );
    }

    for (const model of manifest.spec.models) {
      if (!model.id.trim() || modelIds.has(model.id)) {
        throw new Error(`Идентификатор модели пуст или повторяется: ${model.id || "<empty>"}.`);
      }
      modelIds.add(model.id);
      const capabilityIds = new Set(model.capabilities.map((capability) => capability.id));
      if (capabilityIds.size !== model.capabilities.length) {
        throw new Error(`Модель ${model.id} повторяет capability.`);
      }
      const ConnectionView = plugin.connectionViews[model.ui.componentKey];
      if (!ConnectionView) {
        throw new Error(
          `Плагин ${manifest.metadata.id} не реализует UI ${model.ui.componentKey}.`,
        );
      }
      models.push({
        plugin,
        model,
        ConnectionView,
        ...(plugin.SpatialControlsView
          ? { SpatialControlsView: plugin.SpatialControlsView }
          : {}),
      });
    }

    if (isDevicePluginManifestV1Alpha2(manifest)) {
      const profileIds = new Set<string>();
      const profilePaths = new Set<string>();
      const declaredModelIds = new Set(manifest.spec.models.map((model) => model.id));
      const coveredModelIds = new Set<string>();
      if (!manifest.spec.compatibilityProfiles.length) {
        throw new Error(`Плагин ${manifest.metadata.id} не объявляет compatibilityProfiles.`);
      }
      for (const profile of manifest.spec.compatibilityProfiles) {
        if (profileIds.has(profile.profileId) || profilePaths.has(profile.path)) {
          throw new Error(`Плагин ${manifest.metadata.id} повторяет compatibility profile.`);
        }
        profileIds.add(profile.profileId);
        profilePaths.add(profile.path);
        if (!declaredModelIds.has(profile.modelId)) {
          throw new Error(
            `Профиль ${profile.profileId} ссылается на неизвестную модель ${profile.modelId}.`,
          );
        }
        coveredModelIds.add(profile.modelId);
      }
      const uncovered = [...declaredModelIds].filter(
        (modelId) => !coveredModelIds.has(modelId),
      );
      if (uncovered.length) {
        throw new Error(
          `Плагин ${manifest.metadata.id} не имеет разрешённого профиля для ${uncovered.join(", ")}.`,
        );
      }
    }
  }

  const modelById = new Map(models.map((registered) => [registered.model.id, registered]));
  return {
    plugins: Object.freeze([...installedPlugins]),
    models: Object.freeze(models),
    resolveModel: (modelId) => modelById.get(modelId) ?? null,
  };
}
