import { Logger } from '@stryker-mutator/api/logging';
import { FileDescriptions, StrykerOptions } from '@stryker-mutator/api/core';
import { I, normalizeWhitespaces, propertyPath } from '@stryker-mutator/util';
import { MutationTestResult } from 'mutation-testing-report-schema';

import { FileSystem } from './file-system.js';
import { ProjectFile } from './project-file.js';

/**
 * Represents the project that is under test by Stryker users.
 * This represents the files in the current working directory.
 * Each file can be read into memory when needed (via `readContent`)
 */
export class Project {
  public readonly files = new Map<string, ProjectFile>();
  public readonly filesToMutate = new Map<string, ProjectFile>();
  public readonly testFiles: readonly string[];

  constructor(
    fs: I<FileSystem>,
    public readonly fileDescriptions: FileDescriptions,
    public readonly incrementalReport?: MutationTestResult,
    testFiles: readonly string[] = [],
  ) {
    this.testFiles = testFiles;
    Object.entries(fileDescriptions).forEach(([name, desc]) => {
      const file = new ProjectFile(fs, name, desc.mutate);
      this.files.set(name, file);
      if (desc.mutate) {
        this.filesToMutate.set(name, file);
      }
    });
  }

  public get isEmpty(): boolean {
    return this.files.size === 0;
  }

  public logFiles(
    log: Logger,
    ignoreRules: readonly string[],
    force: boolean,
    mutatePatterns: readonly string[],
    testFilePatterns: readonly string[] = [],
  ): void {
    if (this.isEmpty) {
      log.warn(
        normalizeWhitespaces(`No files found in directory ${process.cwd()} using ignore rules: ${JSON.stringify(ignoreRules)}. 
      Make sure you run Stryker from the root directory of your project with the correct "${propertyPath<StrykerOptions>()('ignorePatterns')}".`),
      );
    } else {
      if (this.filesToMutate.size) {
        const incrementalInfo = this.incrementalReport
          ? ` using incremental report with ${Object.values(
              this.incrementalReport.files,
            ).reduce(
              (total, { mutants }) => total + mutants.length,
              0,
            )} mutant(s), and ${Object.values(this.incrementalReport.testFiles ?? {}).reduce((total, { tests }) => total + tests.length, 0)} test(s)${
              force
                ? '. Force mode is activated, all mutants will be retested'
                : ''
            }`
          : '';
        log.info(
          `Found ${this.filesToMutate.size} of ${this.files.size} file(s) to be mutated${incrementalInfo}.`,
        );
      } else {
        const msg =
          normalizeWhitespaces(`Warning: No files found for mutation with the given glob expressions. As a result, a dry-run will be performed without actually modifying anything. 
          If you intended to mutate files, please check and adjust the configuration.
          Current glob pattern(s) used:
          ${new Intl.ListFormat('en').format(mutatePatterns.map((pattern) => `"${pattern}"`))}.

          To enable file mutation, consider configuring the \`${propertyPath<StrykerOptions>()(
            'mutate',
          )}\` property in your configuration file or using the --mutate option via the command line.`);
        log.warn(msg);
      }
      if (this.testFiles.length > 0) {
        log.info(
          `Found ${this.testFiles.length} test file(s) matching --testFiles patterns.`,
        );
      }
      if (log.isDebugEnabled()) {
        log.debug(
          `All input files: ${JSON.stringify([...this.files.keys()], null, 2)}`,
        );
        log.debug(
          `Files to mutate: ${JSON.stringify([...this.filesToMutate.keys()], null, 2)}`,
        );
        if (this.testFiles.length > 0) {
          log.debug(`Test files: ${JSON.stringify(this.testFiles, null, 2)}`);
        }
      }
    }
  }
}
