import type { Timestamp } from '../../../util/timestamp';
import type { ReleaseResult } from '../types';
import {
  ComposerRelease,
  ComposerReleases,
  MinifiedArray,
  RegistryMeta,
  parsePackagesResponse,
  parsePackagesResponses,
} from './schema';

describe('modules/datasource/packagist/schema', () => {
  describe('MinifiedArray', () => {
    it('parses MinifiedArray', () => {
      expect(MinifiedArray.parse([])).toEqual([]);

      // Source: https://github.com/composer/metadata-minifier/blob/1.0.0/tests/MetadataMinifierTest.php
      expect(
        MinifiedArray.parse([
          {
            name: 'foo/bar',
            version: '2.0.0',
            version_normalized: '2.0.0.0',
            type: 'library',
            scripts: {
              foo: 'bar',
            },
            license: ['MIT'],
          },
          {
            version: '1.2.0',
            version_normalized: '1.2.0.0',
            license: ['GPL'],
            homepage: 'https://example.org',
            scripts: '__unset',
          },
          {
            version: '1.0.0',
            version_normalized: '1.0.0.0',
            homepage: '__unset',
          },
        ]),
      ).toEqual([
        {
          name: 'foo/bar',
          version: '2.0.0',
          version_normalized: '2.0.0.0',
          type: 'library',
          scripts: {
            foo: 'bar',
          },
          license: ['MIT'],
        },
        {
          name: 'foo/bar',
          version: '1.2.0',
          version_normalized: '1.2.0.0',
          type: 'library',
          license: ['GPL'],
          homepage: 'https://example.org',
        },
        {
          name: 'foo/bar',
          version: '1.0.0',
          version_normalized: '1.0.0.0',
          type: 'library',
          license: ['GPL'],
        },
      ]);
    });
  });

  describe('ComposerRelease', () => {
    it('rejects ComposerRelease', () => {
      expect(() => ComposerRelease.parse(null)).toThrow();
      expect(() => ComposerRelease.parse(undefined)).toThrow();
      expect(() => ComposerRelease.parse('')).toThrow();
      expect(() => ComposerRelease.parse({})).toThrow();
      expect(() => ComposerRelease.parse({ version: null })).toThrow();
      expect(() => ComposerRelease.parse({ version: null })).toThrow();
    });

    it('parses ComposerRelease', () => {
      expect(ComposerRelease.parse({ version: '' })).toEqual({
        version: '',
        homepage: null,
        source: null,
        time: null,
        require: null,
      });
      expect(ComposerRelease.parse({ version: 'dev-main' })).toEqual({
        version: 'dev-main',
        homepage: null,
        source: null,
        time: null,
        require: null,
      });

      expect(ComposerRelease.parse({ version: '1.2.3' })).toEqual({
        version: '1.2.3',
        homepage: null,
        source: null,
        time: null,
        require: null,
      });

      expect(ComposerRelease.parse({ version: '1.2.3', homepage: 42 })).toEqual(
        {
          version: '1.2.3',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
      );

      expect(
        ComposerRelease.parse({ version: '1.2.3', homepage: 'example.com' }),
      ).toEqual({
        version: '1.2.3',
        homepage: 'example.com',
        source: null,
        time: null,
        require: null,
      });

      expect(
        ComposerRelease.parse({ version: '1.2.3', source: 'nonsense' }),
      ).toEqual({
        version: '1.2.3',
        homepage: null,
        source: null,
        time: null,
        require: null,
      });

      expect(
        ComposerRelease.parse({ version: '1.2.3', source: { url: 'foobar' } }),
      ).toEqual({
        version: '1.2.3',
        source: { url: 'foobar' },
        homepage: null,
        time: null,
        require: null,
      });

      expect(
        ComposerRelease.parse({
          version: '1.2.3',
          time: '2025-01-16T12:00:00.000Z',
        }),
      ).toEqual({
        version: '1.2.3',
        time: '2025-01-16T12:00:00.000Z',
        homepage: null,
        source: null,
        require: null,
      });

      expect(
        ComposerRelease.parse({
          version: 123,
        }),
      ).toEqual({
        version: '123',
        time: null,
        homepage: null,
        source: null,
        require: null,
      });
    });
  });

  describe('ComposerReleases', () => {
    it('parses ComposerReleases', () => {
      expect(ComposerReleases.parse(null)).toBeEmptyArray();
      expect(ComposerReleases.parse(undefined)).toBeEmptyArray();
      expect(ComposerReleases.parse('')).toBeEmptyArray();
      expect(ComposerReleases.parse({})).toBeEmptyArray();
      expect(ComposerReleases.parse([])).toBeEmptyArray();
      expect(ComposerReleases.parse([null])).toBeEmptyArray();
      expect(ComposerReleases.parse([1, 2, 3])).toBeEmptyArray();
      expect(ComposerReleases.parse(['foobar'])).toBeEmptyArray();
      expect(
        ComposerReleases.parse([{ version: '1.2.3' }, { version: 'dev-main' }]),
      ).toEqual([
        {
          version: '1.2.3',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
        {
          version: 'dev-main',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
      ]);
    });
  });

  describe('parsePackageResponse', () => {
    it('parses package response', () => {
      expect(parsePackagesResponse('foo/bar', null)).toEqual([]);
      expect(parsePackagesResponse('foo/bar', {})).toEqual([]);
      expect(parsePackagesResponse('foo/bar', { packages: '123' })).toEqual([]);
      expect(parsePackagesResponse('foo/bar', { packages: {} })).toEqual([]);

      expect(
        parsePackagesResponse('foo/bar', {
          packages: {
            'foo/bar': [{ version: '1.2.3' }],
            'baz/qux': [{ version: '4.5.6' }],
          },
        }),
      ).toEqual([
        {
          version: '1.2.3',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
      ]);

      expect(
        parsePackagesResponse('foo/bar', {
          packages: {
            'foo/bar': { '1.2.3': { version: '1.2.3' } },
            'baz/qux': { '4.5.6': { version: '4.5.6' } },
          },
        }),
      ).toEqual([
        {
          version: '1.2.3',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
      ]);
    });

    it('expands minified fields', () => {
      expect(
        parsePackagesResponse('foo/bar', {
          packages: {
            'foo/bar': [
              { version: '3.3.3', require: { php: '^8.0' } },
              { version: '2.2.2' },
              { version: '1.1.1' },
              { version: '0.0.4', require: { php: '^7.0' } },
              { version: '0.0.3' },
              { version: '0.0.2', require: '__unset' },
              { version: '0.0.1' },
            ],
          },
        }),
      ).toEqual([
        {
          version: '3.3.3',
          require: { php: '^8.0' },
          homepage: null,
          source: null,
          time: null,
        },
        {
          version: '2.2.2',
          require: { php: '^8.0' },
          homepage: null,
          source: null,
          time: null,
        },
        {
          version: '1.1.1',
          require: { php: '^8.0' },
          homepage: null,
          source: null,
          time: null,
        },
        {
          version: '0.0.4',
          require: { php: '^7.0' },
          homepage: null,
          source: null,
          time: null,
        },
        {
          version: '0.0.3',
          require: { php: '^7.0' },
          homepage: null,
          source: null,
          time: null,
        },
        {
          version: '0.0.2',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
        {
          version: '0.0.1',
          homepage: null,
          source: null,
          time: null,
          require: null,
        },
      ]);
    });
  });

  describe('parsePackagesResponses', () => {
    it('parses array of responses', () => {
      expect(parsePackagesResponses('foo/bar', [null])).toBeNull();
      expect(parsePackagesResponses('foo/bar', [{}])).toBeNull();
      expect(
        parsePackagesResponses('foo/bar', [{ packages: '123' }]),
      ).toBeNull();
      expect(parsePackagesResponses('foo/bar', [{ packages: {} }])).toBeNull();
      expect(
        parsePackagesResponses('foo/bar', [
          {
            packages: {
              'foo/bar': [
                {
                  version: 'v1.1.1',
                  time: '2025-01-16T12:00:00+00:00' as Timestamp,
                  homepage: 'https://example.com/1',
                  source: { url: 'git@example.com:foo/bar-1' },
                  require: { php: '^8.0' },
                },
              ],
              'baz/qux': [
                {
                  version: 'v2.2.2',
                  time: '2025-01-16T12:00:00+00:00' as Timestamp,
                  homepage: 'https://example.com/2',
                  source: { url: 'git@example.com:baz/qux-2' },
                  require: null,
                },
              ],
            },
          },
          {
            packages: {
              'foo/bar': [
                {
                  version: 'v3.3.3',
                  time: '2025-01-16T12:00:00+00:00' as Timestamp,
                  homepage: 'https://example.com/3',
                  source: { url: 'git@example.com:foo/bar-3' },
                  require: { php: '^7.0' },
                },
              ],
              'baz/qux': [
                {
                  version: 'v4.4.4',
                  time: '2025-01-16T12:00:00+00:00' as Timestamp,
                  homepage: 'https://example.com/4',
                  source: { url: 'git@example.com:baz/qux-3' },
                  require: null,
                },
              ],
            },
          },
        ] satisfies { packages: Record<string, ComposerRelease[]> }[]),
      ).toEqual({
        homepage: 'https://example.com/1',
        sourceUrl: 'git@example.com:foo/bar-1',
        releases: [
          {
            version: '1.1.1',
            gitRef: 'v1.1.1',
            releaseTimestamp: '2025-01-16T12:00:00.000Z' as Timestamp,
            constraints: { php: ['^8.0'] },
          },
          {
            version: '3.3.3',
            gitRef: 'v3.3.3',
            releaseTimestamp: '2025-01-16T12:00:00.000Z' as Timestamp,
            constraints: { php: ['^7.0'] },
          },
        ],
      } satisfies ReleaseResult);
    });
  });

  describe('RegistryMeta', () => {
    it('falls back to default values', () => {
      expect(RegistryMeta.parse('nonsense')).toEqual({
        files: [],
        includesFiles: [],
        packages: {},
        providerPackages: {},
        includesPackages: {},
        providersLazyUrl: null,
        providersUrl: null,
        metadataUrl: null,
        availablePackages: null,
      });
    });
  });
});
