import {
  ComponentPreviewService,
  ComponentRunnerServiceWithWorkers,
  RenderInputService,
  setupRenderRuntimeServer,
} from '@squiz/render-runtime-lib';
import { NodeSandbox } from '@squiz/runtime-sandbox-node';
import { getLogger, LoggerOptions } from '@squiz/dx-logger-lib';
import path from 'path';
import { ComponentFunctionService, ComponentSetServiceForLocalDev, ManifestServiceForDev } from '@squiz/component-lib';
import open from 'open';
import { DevelopmentApiKeyService } from '@squiz/dx-common-lib';
import { ComponentLogger } from '@squiz/component-logger-lib';

/**
 * startDevelopmentRender starts a dev-mode render stack for any
 * local directory. This should start an express server on a random unused port
 * which can be accessed for viewing the component in development.
 * @param {string} componentPath - The directory with the component to be rendered
 * @param {object} options - Additional configuration for the dev stack
 * @returns a function to stop the render stack
 */
export function startDevelopmentRender(
  componentPath: string,
  options: { port: number; loggingFormat?: LoggerOptions['format']; noBrowser?: boolean },
) {
  const logger = getLogger({
    name: 'component-dev',
    format: options.loggingFormat || 'human',
    silent: process.env.NODE_ENV === 'test',
  });

  const devComponentLogger = new ComponentLogger({
    enabled: false,
    tenantId: 'dev-tenant-id',
    logUrl: '',
    key: '',
    tags: 'tag-foo tag-foo2',
    serviceName: 'component-dev',
  });

  const dataMountPoint = path.resolve(process.cwd(), componentPath);

  const rootUrl = `http://localhost:${options.port}`;

  const apiKeyService = new DevelopmentApiKeyService();
  const sandbox = new NodeSandbox({
    workerPool: {
      shouldCacheResponses: false,
      workerLimits: {
        minWorkers: 2,
        maxWorkers: 2,
      },
    },
  });
  const componentRunnerService = new ComponentRunnerServiceWithWorkers(sandbox, logger, devComponentLogger);
  const componentFunctionService = new ComponentFunctionService(rootUrl);
  const componentSetService = new ComponentSetServiceForLocalDev(logger);
  const manifestService = new ManifestServiceForDev(dataMountPoint, logger);
  const contentItemService = undefined;
  const renderInputService = new RenderInputService(
    apiKeyService,
    componentSetService,
    manifestService,
    componentFunctionService,
    contentItemService,
    rootUrl,
  );
  const webServer = setupRenderRuntimeServer({
    logger,
    componentRunnerService,
    componentFunctionService,
    componentPreviewService: new ComponentPreviewService(),
    componentSetService,
    manifestService,
    renderInputService,
    apiKeyService,
    sandbox,
  });

  const server = webServer.listen(options.port, () => {
    logger.info(`Component development webserver started on port ${rootUrl}`);
  });

  if (options?.noBrowser !== true) {
    open(rootUrl);
  }

  server.on('close', async () => {
    await componentRunnerService.stop();
  });

  return server;
}
