import { autoService } from 'knifecycle';
import { generateOpenAPITypes as generateTypes, toSource } from 'schema2dts';
import { type OpenAPITypesGenerationOptions } from 'schema2dts';
import { type LogService } from 'common-services';
import {
  type WhookCommandHandler,
  type WhookCommandDefinition,
} from '../types/commands.js';

export type OpenAPITypesConfig = {
  OPEN_API_TYPES_CONFIG: OpenAPITypesGenerationOptions;
};

/* Architecture Note #2.9.2.2: Typings generator

This command allows you to generate the API types that
 helps you to write your handler in a clean and safe
 manner.
*/
export const definition = {
  name: 'generateOpenAPITypes',
  description: 'Write openAPI types to stdout',
  example: `whook generateOpenAPITypes`,
  arguments: [],
} as const satisfies WhookCommandDefinition;

async function initGenerateOpenAPITypes({
  OPEN_API_TYPES_CONFIG,
  instream = process.stdin,
  outstream = process.stdout,
  log,
}: OpenAPITypesConfig & {
  instream: NodeJS.ReadableStream;
  outstream: NodeJS.WritableStream;
  log: LogService;
}): Promise<WhookCommandHandler> {
  return async function generateOpenAPITypes(): Promise<void> {
    log('warning', '📥 - Retrieving API schema...');

    const openAPI: string = await new Promise((resolve, reject) => {
      let buffer = Buffer.from('');

      instream.on('data', (aBuffer) => {
        buffer = Buffer.concat([buffer, aBuffer]);
      });
      instream.once('error', () => reject);
      instream.once('end', () => resolve(buffer.toString()));
    });

    const typesDefs = toSource(
      await generateTypes(JSON.parse(openAPI), OPEN_API_TYPES_CONFIG),
    );

    log('warning', '📇 - Writing types...');
    await new Promise((resolve, reject) => {
      outstream.once('finish', resolve);
      outstream.once('error', reject);
      outstream.write(`// This file is autogenerated by Whook
// Do not try to change it in place
/* eslint-disable @typescript-eslint/ban-types */
`);
      outstream.write(typesDefs);
      outstream.end();
    });
  };
}

export default autoService(initGenerateOpenAPITypes);
