import { mkdtempSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { describe, expect, it } from 'vitest';
import { loadSkillRegistryCatalog, parseSkillRegistryCatalog } from './registrySource.js';

describe('parseSkillRegistryCatalog', () => {
  it('parses a valid registry catalog', () => {
    const catalog = parseSkillRegistryCatalog({
      skills: [
        {
          id: 'todoist',
          name: 'Todoist',
          version: '1.2.3',
          source: 'https://example.com/skills/todoist.git',
          summary: 'Task manager integration',
          publisher: 'acme',
          homepage: 'https://example.com/todoist',
        },
      ],
    });

    expect(catalog.skills).toHaveLength(1);
    expect(catalog.skills[0]?.id).toBe('todoist');
    expect(catalog.skills[0]?.publisher).toBe('acme');
  });

  it('rejects missing required fields', () => {
    expect(() =>
      parseSkillRegistryCatalog({
        skills: [
          {
            id: 'missing-summary',
            name: 'Broken',
            version: '0.1.0',
            source: 'https://example.com/skills/broken.git',
          },
        ],
      }),
    ).toThrow(/summary/);
  });

  it('rejects invalid ids', () => {
    expect(() =>
      parseSkillRegistryCatalog({
        skills: [
          {
            id: 'Not Valid!',
            name: 'Broken',
            version: '0.1.0',
            source: 'https://example.com/skills/broken.git',
            summary: 'Broken',
          },
        ],
      }),
    ).toThrow(/id/);
  });
});

describe('loadSkillRegistryCatalog', () => {
  it('loads catalog from a local file path', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'flynn-registry-test-'));
    const filePath = join(dir, 'registry.json');

    writeFileSync(
      filePath,
      JSON.stringify({
        skills: [
          {
            id: 'notes',
            name: 'Notes',
            version: '0.0.1',
            source: './skills/notes',
            summary: 'Local notes skill',
          },
        ],
      }),
      'utf8',
    );

    try {
      const catalog = await loadSkillRegistryCatalog({ type: 'file', path: filePath });
      expect(catalog.skills).toHaveLength(1);
      expect(catalog.skills[0]?.source).toBe('./skills/notes');
    } finally {
      rmSync(dir, { recursive: true, force: true });
    }
  });

  it('loads catalog from HTTPS URL with injected fetch', async () => {
    const catalog = await loadSkillRegistryCatalog(
      { type: 'url', url: 'https://registry.example.com/catalog.json' },
      {
        fetchImpl: async () =>
          ({
            ok: true,
            status: 200,
            json: async () => ({
              skills: [
                {
                  id: 'calendar',
                  name: 'Calendar',
                  version: '2.0.0',
                  source: 'https://example.com/skills/calendar.git',
                  summary: 'Calendar integration',
                },
              ],
            }),
          }) as Response,
      },
    );

    expect(catalog.skills).toHaveLength(1);
    expect(catalog.skills[0]?.id).toBe('calendar');
  });

  it('rejects non-HTTPS URL sources', async () => {
    await expect(
      loadSkillRegistryCatalog(
        { type: 'url', url: 'http://registry.example.com/catalog.json' },
        {
          fetchImpl: async () =>
            ({
              ok: true,
              status: 200,
              json: async () => ({ skills: [] }),
            }) as Response,
        },
      ),
    ).rejects.toThrow(/https/);
  });

  it('propagates HTTP fetch failures with status', async () => {
    await expect(
      loadSkillRegistryCatalog(
        { type: 'url', url: 'https://registry.example.com/catalog.json' },
        {
          fetchImpl: async () =>
            ({
              ok: false,
              status: 502,
              json: async () => ({}),
            }) as Response,
        },
      ),
    ).rejects.toThrow(/502/);
  });
});
