import { describe, expect, it, vi } from 'vitest';
import { ReloadService } from './reload.service.js';

function createMockCommandRegistry() {
  return {
    getManifest: vi.fn().mockReturnValue({
      version: 1,
      commands: [],
      skills: [],
    }),
  };
}

function createService() {
  const registry = createMockCommandRegistry();
  const service = new ReloadService(registry as never);
  return { service, registry };
}

describe('ReloadService', () => {
  it('reload() calls onUnload then onLoad for registered MosaicPlugin', async () => {
    const { service } = createService();

    const callOrder: string[] = [];
    const mockPlugin = {
      pluginName: 'test-plugin',
      onLoad: vi.fn().mockImplementation(() => {
        callOrder.push('onLoad');
        return Promise.resolve();
      }),
      onUnload: vi.fn().mockImplementation(() => {
        callOrder.push('onUnload');
        return Promise.resolve();
      }),
    };

    service.registerPlugin('test-plugin', mockPlugin);
    const result = await service.reload('command');

    expect(mockPlugin.onUnload).toHaveBeenCalledOnce();
    expect(mockPlugin.onLoad).toHaveBeenCalledOnce();
    expect(callOrder).toEqual(['onUnload', 'onLoad']);
    expect(result.message).toContain('test-plugin');
  });

  it('reload() continues if one plugin throws during onUnload', async () => {
    const { service } = createService();

    const badPlugin = {
      pluginName: 'bad-plugin',
      onLoad: vi.fn().mockResolvedValue(undefined),
      onUnload: vi.fn().mockRejectedValue(new Error('unload failed')),
    };

    service.registerPlugin('bad-plugin', badPlugin);
    const result = await service.reload('command');

    expect(result.message).toContain('bad-plugin');
    expect(result.message).toContain('unload failed');
  });

  it('reload() skips non-MosaicPlugin objects', async () => {
    const { service } = createService();

    const notAPlugin = { foo: 'bar' };
    service.registerPlugin('not-a-plugin', notAPlugin);

    // Should not throw
    const result = await service.reload('command');
    expect(result).toBeDefined();
    expect(result.message).not.toContain('not-a-plugin');
  });

  it('reload() returns SystemReloadPayload with commands, skills, providers, message', async () => {
    const { service, registry } = createService();
    registry.getManifest.mockReturnValue({
      version: 1,
      commands: [
        {
          name: 'test',
          description: 'test cmd',
          aliases: [],
          scope: 'core',
          execution: 'socket',
          available: true,
        },
      ],
      skills: [],
    });

    const result = await service.reload('rest');

    expect(result).toHaveProperty('commands');
    expect(result).toHaveProperty('skills');
    expect(result).toHaveProperty('providers');
    expect(result).toHaveProperty('message');
    expect(result.commands).toHaveLength(1);
  });

  it('registerPlugin() logs plugin registration', () => {
    const { service } = createService();

    // Should not throw and should register
    expect(() => service.registerPlugin('my-plugin', {})).not.toThrow();
  });
});
