// 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:meta/meta.dart';

import '../android/gradle_errors.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../build_info.dart';
import '../globals.dart' as globals;
import '../project.dart';
import 'gradle.dart';

/// The builder in the current context.
AndroidBuilder get androidBuilder {
  return context.get<AndroidBuilder>() ?? const _AndroidBuilderImpl();
}

/// Provides the methods to build Android artifacts.
// TODO(egarciad): https://github.com/flutter/flutter/issues/43863
abstract class AndroidBuilder {
  const AndroidBuilder();
  /// Builds an AAR artifact.
  Future<void> buildAar({
    @required FlutterProject project,
    @required Set<AndroidBuildInfo> androidBuildInfo,
    @required String target,
    @required String outputDirectoryPath,
    @required String buildNumber,
  });

  /// Builds an APK artifact.
  Future<void> buildApk({
    @required FlutterProject project,
    @required AndroidBuildInfo androidBuildInfo,
    @required String target,
  });

  /// Builds an App Bundle artifact.
  Future<void> buildAab({
    @required FlutterProject project,
    @required AndroidBuildInfo androidBuildInfo,
    @required String target,
  });
}

/// Default implementation of [AarBuilder].
class _AndroidBuilderImpl extends AndroidBuilder {
  const _AndroidBuilderImpl();

  /// Builds the AAR and POM files for the current Flutter module or plugin.
  @override
  Future<void> buildAar({
    @required FlutterProject project,
    @required Set<AndroidBuildInfo> androidBuildInfo,
    @required String target,
    @required String outputDirectoryPath,
    @required String buildNumber,
  }) async {
    try {
      Directory outputDirectory =
        globals.fs.directory(outputDirectoryPath ?? project.android.buildDirectory);
      if (project.isModule) {
        // Module projects artifacts are located in `build/host`.
        outputDirectory = outputDirectory.childDirectory('host');
      }
      for (final AndroidBuildInfo androidBuildInfo in androidBuildInfo) {
        await buildGradleAar(
          project: project,
          androidBuildInfo: androidBuildInfo,
          target: target,
          outputDirectory: outputDirectory,
          buildNumber: buildNumber,
        );
      }
      printHowToConsumeAar(
        buildModes: androidBuildInfo
          .map<String>((AndroidBuildInfo androidBuildInfo) {
            return androidBuildInfo.buildInfo.modeName;
          }).toSet(),
        androidPackage: project.manifest.androidPackage,
        repoDirectory: getRepoDirectory(outputDirectory),
        buildNumber: buildNumber,
        logger: globals.logger,
        fileSystem: globals.fs,
      );
    } finally {
      globals.androidSdk?.reinitialize();
    }
  }

  /// Builds the APK.
  @override
  Future<void> buildApk({
    @required FlutterProject project,
    @required AndroidBuildInfo androidBuildInfo,
    @required String target,
  }) async {
    try {
      await buildGradleApp(
        project: project,
        androidBuildInfo: androidBuildInfo,
        target: target,
        isBuildingBundle: false,
        localGradleErrors: gradleErrors,
      );
    } finally {
      globals.androidSdk?.reinitialize();
    }
  }

  /// Builds the App Bundle.
  @override
  Future<void> buildAab({
    @required FlutterProject project,
    @required AndroidBuildInfo androidBuildInfo,
    @required String target,
  }) async {
    try {
      await buildGradleApp(
        project: project,
        androidBuildInfo: androidBuildInfo,
        target: target,
        isBuildingBundle: true,
        localGradleErrors: gradleErrors,
      );
    } finally {
      globals.androidSdk?.reinitialize();
    }
  }
}
