// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:process/process.dart';

import '../base/file_system.dart';
import '../base/logger.dart';
import '../project.dart';
import '../project_validator.dart';
import '../project_validator_result.dart';
import '../runner/flutter_command.dart';

class ValidateProject {
  ValidateProject({
    required this.fileSystem,
    required this.logger,
    required this.allProjectValidators,
    required this.userPath,
    required this.processManager,
    this.verbose = false,
    this.machine = false,
  });

  final FileSystem fileSystem;
  final Logger logger;
  final bool verbose;
  final bool machine;
  final String userPath;
  final List<ProjectValidator> allProjectValidators;
  final ProcessManager processManager;

  Future<FlutterCommandResult> run() async {
    final Directory workingDirectory = userPath.isEmpty ? fileSystem.currentDirectory : fileSystem.directory(userPath);

    final FlutterProject project =  FlutterProject.fromDirectory(workingDirectory);
    final Map<ProjectValidator, Future<List<ProjectValidatorResult>>> results = <ProjectValidator, Future<List<ProjectValidatorResult>>>{};

    bool hasCrash = false;
    for (final ProjectValidator validator in allProjectValidators) {
      if (validator.machineOutput != machine) {
        continue;
      }
      if (!results.containsKey(validator) && validator.supportsProject(project)) {
        results[validator] = validator
            .start(project)
            .then(
              (List<ProjectValidatorResult> results) => results,
              onError: (Object exception, StackTrace trace) {
                hasCrash = true;
                return <ProjectValidatorResult>[
                  ProjectValidatorResult.crash(exception, trace),
                ];
              },
            );
      }
    }

    final StringBuffer buffer = StringBuffer();
    if (machine) {
      // Print properties
      buffer.write('{\n');
      for (final Future<List<ProjectValidatorResult>> resultListFuture in results.values) {
        final List<ProjectValidatorResult> resultList = await resultListFuture;
        int count = 0;
        for (final ProjectValidatorResult result in resultList) {
          count++;
          buffer.write('  "${result.name}": ${result.value}${count < resultList.length ? ',' : ''}\n');
        }
      }
      buffer.write('}');
      logger.printStatus(buffer.toString());
    } else {
      final List<String> resultsString = <String>[];
      for (final ProjectValidator validator in results.keys) {
        if (results[validator] != null) {
          resultsString.add(validator.title);
          addResultString(validator.title, await results[validator], resultsString);
        }
      }
      buffer.writeAll(resultsString, '\n');
      logger.printBox(buffer.toString());
    }

    if (hasCrash) {
      return const FlutterCommandResult(ExitStatus.fail);
    }
    return const FlutterCommandResult(ExitStatus.success);
  }


  void addResultString(final String title, final List<ProjectValidatorResult>? results, final List<String> resultsString) {
    if (results != null) {
      for (final ProjectValidatorResult result in results) {
        resultsString.add(getStringResult(result));
      }
    }
  }

  String getStringResult(ProjectValidatorResult result) {
    final String icon;
    switch (result.status) {
      case StatusProjectValidator.error:
        icon = '[✗]';
      case StatusProjectValidator.info:
      case StatusProjectValidator.success:
        icon = '[✓]';
      case StatusProjectValidator.warning:
        icon = '[!]';
      case StatusProjectValidator.crash:
        icon = '[☠]';
    }

    return '$icon $result';
  }
}
