/**
 * @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 {
  Rule,
  SchematicsException,
  apply,
  applyTemplates,
  chain,
  mergeWith,
  move,
  strings,
  url,
} from '@angular-devkit/schematics';
import {
  AngularBuilder,
  DependencyType,
  ExistingBehavior,
  addDependency,
  addRootProvider,
  updateWorkspace,
} from '@schematics/angular/utility';
import { posix as path } from 'node:path';
import { JSONFile } from '../utility/json-file';
import { latestVersions } from '../utility/latest-versions';
import { Schema as E2eOptions } from './schema';

/**
 * The list of development dependencies used by the E2E protractor-based builder.
 * The versions are sourced from the latest versions `../utility/latest-versions/package.json`
 * file which is automatically updated via renovate.
 */
const E2E_DEV_DEPENDENCIES = Object.freeze([
  'protractor',
  'jasmine-spec-reporter',
  'ts-node',
  '@types/node',
]);

function addScriptsToPackageJson(): Rule {
  return (host) => {
    const pkgJson = new JSONFile(host, 'package.json');
    const e2eScriptPath = ['scripts', 'e2e'];

    if (!pkgJson.get(e2eScriptPath)) {
      pkgJson.modify(e2eScriptPath, 'ng e2e', false);
    }
  };
}

export default function (options: E2eOptions): Rule {
  const { relatedAppName } = options;

  return updateWorkspace((workspace) => {
    const project = workspace.projects.get(relatedAppName);

    if (!project) {
      throw new SchematicsException(`Project name "${relatedAppName}" doesn't not exist.`);
    }

    const e2eRootPath = path.join(project.root, 'e2e');

    project.targets.add({
      name: 'e2e',
      builder: AngularBuilder.Protractor,
      defaultConfiguration: 'development',
      options: {
        protractorConfig: path.join(e2eRootPath, 'protractor.conf.js'),
      },
      configurations: {
        production: {
          devServerTarget: `${relatedAppName}:serve:production`,
        },
        development: {
          devServerTarget: `${relatedAppName}:serve:development`,
        },
      },
    });

    return chain([
      mergeWith(
        apply(url('./files'), [
          applyTemplates({
            utils: strings,
            ...options,
            relativePathToWorkspaceRoot: path.relative(path.join('/', e2eRootPath), '/'),
          }),
          move(e2eRootPath),
        ]),
      ),
      addRootProvider(
        relatedAppName,
        ({ code, external }) =>
          code`${external('provideProtractorTestingSupport', '@angular/platform-browser')}()`,
      ),
      ...E2E_DEV_DEPENDENCIES.map((name) =>
        addDependency(name, latestVersions[name], {
          type: DependencyType.Dev,
          existing: ExistingBehavior.Skip,
        }),
      ),
      addScriptsToPackageJson(),
    ]);
  });
}
