import { fileURLToPath } from 'node:url';
import { afterEach, describe, expect, test, vi } from 'vitest';
import { generateFilesOnly, type OutputFile } from '@/generate-file';
import { createOpenAPI } from '@/server';
import path from 'node:path';

const cwd = fileURLToPath(new URL('./', import.meta.url));

describe('Generate documents', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  test('Pet Store (Per Operation)', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          petstore: path.join(cwd, './fixtures/petstore.yaml'),
        },
      }),
      per: 'operation',
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/petstore-per-operation.md');
  });

  test('Museum (Per Tag)', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          museum: path.join(cwd, './fixtures/museum.yaml'),
        },
      }),
      per: 'tag',
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/museum-per-tag.md');
  });

  test('Unkey (Per File)', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          unkey: path.join(cwd, './fixtures/unkey.json'),
        },
      }),
      per: 'file',
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/unkey-per-file.md');
  });

  test('Generate Files', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          museum: path.join(cwd, './fixtures/museum.yaml'),
          petstore: path.join(cwd, './fixtures/petstore.yaml'),
        },
      }),
      per: 'file',
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/museum+petstore.md');
  });

  test('Generate Files - throws error when no input files found', async () => {
    await expect(
      generateFilesOnly({
        input: createOpenAPI({
          input: [path.join(cwd, './fixtures/non-existent.yaml')],
        }),
        per: 'file',
      }),
    ).rejects.toThrowError();
  });

  test('Generate Files - groupBy tag per operation', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          products: path.join(cwd, './fixtures/products.yaml'),
        },
      }),
      per: 'operation',
      groupBy: 'tag',
      name: {
        algorithm: 'v1',
      },
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/products-group-by-tag.md');
  });

  test('Generate Files - with index', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          products: path.join(cwd, './fixtures/products.yaml'),
        },
      }),
      per: 'operation',
      name: {
        algorithm: 'v1',
      },
      index: {
        url: {
          baseUrl: '/docs',
          contentDir: '',
        },
        items: [
          {
            description: 'all available pages',
            path: 'index.mdx',
            only: ['products'],
          },
        ],
      },
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/products-with-index.md');
  });

  test('Generate Files - with meta', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          products: path.join(cwd, './fixtures/products.yaml'),
        },
      }),
      per: 'operation',
      meta: true,
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/products-with-meta.md');
  });

  test('Generate Files - with meta + groupBy', async () => {
    const out = await generateFilesOnly({
      input: createOpenAPI({
        input: {
          products: path.join(cwd, './fixtures/products.yaml'),
        },
      }),
      per: 'operation',
      groupBy: 'tag',
      meta: true,
    });

    await expect(stringifyOutput(out)).toMatchFileSnapshot('./out/products-with-meta+groupby.md');
  });
});

function stringifyOutput(output: OutputFile[]) {
  output.sort((a, b) => a.path.localeCompare(b.path));

  const lines: string[] = [];
  for (const file of output) {
    const lang = path.extname(file.path).slice(1);
    lines.push(`\`\`\`${lang} title="${file.path}"\n${file.content}\n\`\`\``);
  }
  return lines.join('\n\n');
}
