import { Injector } from '@angular/core';
import type { TojuPluginManifest } from '../../../../shared-kernel';
import { DEVELOPMENT_PLUGIN_MANIFEST } from '../../development/development-plugin';
import type { LocalPluginDiscoveryResult } from '../../domain/models/plugin-runtime.models';
import { LocalPluginDiscoveryService } from '../../infrastructure/local-plugin-discovery.service';
import { PluginCapabilityService } from './plugin-capability.service';
import { PluginClientApiService } from './plugin-client-api.service';
import { PluginDesktopStateService } from './plugin-desktop-state.service';
import { PluginHostService } from './plugin-host.service';
import { PluginLoggerService } from './plugin-logger.service';
import { PluginRegistryService } from './plugin-registry.service';
import { PluginUiRegistryService } from './plugin-ui-registry.service';

const TEST_PLUGIN_MANIFEST = createTestPluginManifest();

describe('PluginHostService', () => {
  let discoveryResult: LocalPluginDiscoveryResult;

  beforeEach(() => {
    discoveryResult = {
      errors: [],
      plugins: [],
      pluginsPath: '/plugins'
    };
  });

  it('registers discovered test plugin manifests', async () => {
    discoveryResult = {
      errors: [],
      plugins: [
        {
          discoveredAt: 1,
          entrypointPath: '/plugins/api-test-plugin/dist/main.js',
          manifest: TEST_PLUGIN_MANIFEST,
          manifestPath: '/plugins/api-test-plugin/toju-plugin.json',
          pluginRoot: '/plugins/api-test-plugin',
          readmePath: '/plugins/api-test-plugin/README.md'
        }
      ],
      pluginsPath: '/plugins'
    };

    const host = createHostService(() => discoveryResult);
    const result = await host.discoverLocalPlugins();

    expect(result.errors).toEqual([]);
    expect(result.registered.map((plugin) => plugin.manifest.id)).toEqual([TEST_PLUGIN_MANIFEST.id]);
    const readyManifestIds = host.getReadyManifests().map((manifest) => manifest.id);

    expect(readyManifestIds.sort()).toEqual([DEVELOPMENT_PLUGIN_MANIFEST.id, TEST_PLUGIN_MANIFEST.id].sort());
  });

  it('registers the built-in development plugin in development builds', () => {
    const host = createHostService(() => discoveryResult);

    expect(host.getReadyManifests().map((manifest) => manifest.id)).toEqual([DEVELOPMENT_PLUGIN_MANIFEST.id]);
  });

  it('keeps discovery and validation failures visible to callers', async () => {
    discoveryResult = {
      errors: [
        {
          manifestPath: '/plugins/broken/plugin.json',
          message: 'Unexpected end of JSON input',
          pluginRoot: '/plugins/broken'
        }
      ],
      plugins: [
        {
          discoveredAt: 1,
          manifest: {
            ...TEST_PLUGIN_MANIFEST,
            entrypoint: undefined
          },
          manifestPath: '/plugins/invalid/toju-plugin.json',
          pluginRoot: '/plugins/invalid'
        }
      ],
      pluginsPath: '/plugins'
    };

    const host = createHostService(() => discoveryResult);
    const result = await host.discoverLocalPlugins();

    expect(result.registered).toEqual([]);
    expect(result.errors.map((error) => error.pluginRoot)).toEqual(['/plugins/broken', '/plugins/invalid']);

    expect(result.errors[1]?.message).toContain('client plugins require an entrypoint');
  });
});

function createHostService(readDiscoveryResult: () => LocalPluginDiscoveryResult): PluginHostService {
  const injector = Injector.create({
    providers: [
      PluginHostService,
      PluginRegistryService,
      {
        provide: PluginCapabilityService,
        useValue: {
          missing: vi.fn(() => [])
        }
      },
      {
        provide: PluginDesktopStateService,
        useValue: {
          readJson: vi.fn(async (_key: string, fallback: unknown) => fallback),
          writeJson: vi.fn(async () => undefined)
        }
      },
      {
        provide: PluginClientApiService,
        useValue: {
          createApi: vi.fn(() => ({}))
        }
      },
      {
        provide: PluginLoggerService,
        useValue: {
          error: vi.fn(),
          info: vi.fn(),
          warn: vi.fn()
        }
      },
      {
        provide: PluginUiRegistryService,
        useValue: {
          unregisterPlugin: vi.fn()
        }
      },
      {
        provide: LocalPluginDiscoveryService,
        useValue: {
          discoverManifests: vi.fn(async () => readDiscoveryResult())
        }
      }
    ]
  });

  return injector.get(PluginHostService);
}

function createTestPluginManifest(): TojuPluginManifest {
  return {
    apiVersion: '1.0.0',
    capabilities: [
      'storage.serverData.read',
      'storage.serverData.write',
      'events.server.publish'
    ],
    compatibility: {
      minimumTojuVersion: '1.0.0'
    },
    description: 'Fixture plugin used by automated tests for plugin support APIs.',
    entrypoint: './dist/main.js',
    events: [
      {
        direction: 'serverRelay',
        eventName: 'e2e:relay',
        maxPayloadBytes: 2048,
        scope: 'server'
      }
    ],
    id: 'e2e.plugin-api',
    kind: 'client',
    schemaVersion: 1,
    title: 'E2E Plugin API Fixture',
    version: '1.0.0'
  };
}
