/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.dev/license
 */

import { Architect } from '@angular-devkit/architect';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as browserSync from 'browser-sync';
import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';
import { createArchitect, host } from '../../../testing/test-utils';
import { SSRDevServerBuilderOutput } from '../index';

describe('Serve SSR Builder', () => {
  const target = { project: 'app', target: 'serve-ssr' };
  const originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
  let architect: Architect;

  beforeAll(() => {
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 100_000;
  });

  afterAll(() => {
    jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
  });

  beforeEach(async () => {
    await host.initialize().toPromise();
    architect = (await createArchitect(host.root())).architect;

    host.writeMultipleFiles({
      'src/main.server.ts': `
        import 'zone.js/node';

        import { CommonEngine } from '@angular/ssr/node';
        import * as express from 'express';
        import { resolve, join } from 'node:path';
        import { AppServerModule } from './app/app.module.server';

        export function app(): express.Express {
          const server = express();
          const distFolder = resolve(__dirname, '../dist');
          const indexHtml = join(distFolder, 'index.html');
          const commonEngine = new CommonEngine();

          server.set('view engine', 'html');
          server.set('views', distFolder);

          server.use(express.static(distFolder, {
            maxAge: '1y',
            index: false,
          }));

          server.use((req, res, next) => {
            const { protocol, originalUrl, baseUrl, headers } = req;
            commonEngine
              .render({
                bootstrap: AppServerModule,
                documentFilePath: indexHtml,
                url: \`\${protocol}://\${headers.host}\${originalUrl}\`,
                publicPath: distFolder,
              })
              .then((html) => res.send(html))
              .catch((err) => next(err));
          });

          return server;
        }

        app().listen(process.env['PORT']);

        export * from './app/app.module.server';
      `,
    });
  });

  afterEach(async () => {
    browserSync.reset();
    await host.restore().toPromise();
  });

  it('works with SSL', async () => {
    const run = await architect.scheduleTarget(target, { ssl: true, port: 0 });
    const output = (await run.result) as SSRDevServerBuilderOutput;

    expect(output.success).toBe(true);
    expect(output.baseUrl).toBe(`https://localhost:${output.port}`);

    // The self-signed certificate used by the dev server will cause fetch to fail
    // unless reject unauthorized is disabled.
    const originalDispatcher = getGlobalDispatcher();
    setGlobalDispatcher(
      new Agent({
        connect: { rejectUnauthorized: false },
      }),
    );
    try {
      const response = await fetch(`https://localhost:${output.port}/index.html`);
      expect(await response.text()).toContain('<title>HelloWorldApp</title>');
    } finally {
      setGlobalDispatcher(originalDispatcher);
    }

    await run.stop();
  });
});
