import { describe, test, beforeEach, jest, expect } from '@jest/globals';
import initGenerateOpenAPITypes from './generateOpenAPITypes.js';
import { PassThrough } from 'node:stream';
import { definition as getPingDefinition } from '../routes/getPing.js';
import { type OpenAPITypesGenerationOptions } from 'schema2dts';
import { type OpenAPI } from 'ya-open-api-types';
import { type LogService } from 'common-services';

describe('generateOpenAPITypes', () => {
  const getOpenAPI = jest.fn();
  const log = jest.fn<LogService>();
  const OPEN_API_TYPES_CONFIG: OpenAPITypesGenerationOptions = {
    basePath: 'src/openAPI.d.ts',
    baseName: 'API',
    generateUnusedSchemas: true,
    generateRealEnums: false,
    exportNamespaces: false,
    brandedTypes: [],
    brandedFormats: [],
    typedFormats: {},
    tuplesFromFixedArraysLengthLimit: 5,
  };
  const API: OpenAPI = {
    openapi: '3.1.0',
    info: {
      version: '1.0.0',
      title: 'Sample OpenAPI',
      description: 'A sample OpenAPI file for testing purpose.',
    },
    paths: {
      [getPingDefinition.path]: {
        [getPingDefinition.method]: getPingDefinition.operation,
      },
    },
  };

  beforeEach(() => {
    getOpenAPI.mockReset();
    log.mockReset();
  });

  test('should work', async () => {
    const instream = new PassThrough();
    const outstream = new PassThrough();
    const outputPromise = new Promise((resolve, reject) => {
      let buffer = Buffer.from('');
      outstream.on('data', (aBuffer) => {
        buffer = Buffer.concat([buffer, aBuffer]);
      });
      outstream.once('error', () => reject);
      outstream.once('end', () => resolve(buffer.toString()));
    });
    const generateOpenAPITypes = await initGenerateOpenAPITypes({
      OPEN_API_TYPES_CONFIG,
      instream,
      outstream,
      log,
    });

    const resultPromise = generateOpenAPITypes({
      command: 'whook',
      namedArguments: {},
      rest: [],
    });

    instream.write(JSON.stringify(API));
    instream.end();

    expect({
      result: await resultPromise,
      output: await outputPromise,
      getOpenAPICalls: getOpenAPI.mock.calls,
      logCalls: log.mock.calls.filter(([type]) => !type.endsWith('stack')),
    }).toMatchInlineSnapshot(
      {},
      `
{
  "getOpenAPICalls": [],
  "logCalls": [
    [
      "warning",
      "📥 - Retrieving API schema...",
    ],
    [
      "warning",
      "📇 - Writing types...",
    ],
  ],
  "output": "// This file is autogenerated by Whook
// Do not try to change it in place
/* eslint-disable @typescript-eslint/ban-types */
declare interface paths {
    "/ping": {
        "get": operations["getPing"];
    };
}
declare interface operations {
    getPing: {
        responses: {
            200: {
                body: {
                    pong?: "pong";
                };
            };
        };
    };
}",
  "result": undefined,
}
`,
    );
  });
});
