// Copyright 2019 The Chromium 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:archive/archive.dart';
import 'package:meta/meta.dart';

import '../asset.dart';
import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../build_info.dart';
import '../bundle.dart';
import '../globals.dart';
import '../project.dart';
import '../reporting/reporting.dart';

/// The [WebCompilationProxy] instance.
WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();

Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo, bool initializePlatform) async {
  if (!flutterProject.web.existsSync()) {
    throwToolExit('Missing index.html.');
  }
  final Status status = logger.startProgress('Compiling $target for the Web...', timeout: null);
  final Stopwatch sw = Stopwatch()..start();
  final Directory outputDir = fs.directory(getWebBuildDirectory())
    ..createSync(recursive: true);
  bool result;
  try {
    result = await webCompilationProxy.initialize(
      projectDirectory: FlutterProject.current().directory,
      mode: buildInfo.mode,
      projectName: flutterProject.manifest.appName,
      initializePlatform: initializePlatform,
    );
    if (result) {
      // Places assets adjacent to the web stuff.
      final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
      await assetBundle.build();
      await writeBundle(fs.directory(fs.path.join(outputDir.path, 'assets')), assetBundle.entries);

      // Copy results to output directory.
      final String outputPath = fs.path.join(
        flutterProject.dartTool.path,
        'build',
        'flutter_web',
        flutterProject.manifest.appName,
        '${fs.path.withoutExtension(target)}_web_entrypoint.dart.js',
      );
      // Check for deferred import outputs.
      final File dart2jsArchive = fs.file(fs.path.join(
        flutterProject.dartTool.path,
        'build',
        'flutter_web',
        '${flutterProject.manifest.appName}',
        '${fs.path.withoutExtension(target)}_web_entrypoint.dart.js.tar.gz'),
      );
      fs.file(outputPath).copySync(fs.path.join(outputDir.path, 'main.dart.js'));
      fs.file('$outputPath.map').copySync(fs.path.join(outputDir.path, 'main.dart.js.map'));
      flutterProject.web.indexFile.copySync(fs.path.join(outputDir.path, 'index.html'));
      if (dart2jsArchive.existsSync()) {
        final Archive archive = TarDecoder().decodeBytes(dart2jsArchive.readAsBytesSync());
        for (ArchiveFile file in archive.files) {
          outputDir.childFile(file.name).writeAsBytesSync(file.content);
        }
      }
    }
  } catch (err) {
    printError(err.toString());
    result = false;
  } finally {
    status.stop();
  }
  if (result == false) {
    throwToolExit('Failed to compile $target for the Web.');
  }
  String buildName = 'ddc';
  if (buildInfo.isRelease) {
    buildName = 'dart2js';
  }
  flutterUsage.sendTiming('build', buildName, Duration(milliseconds: sw.elapsedMilliseconds));
}

/// An indirection on web compilation.
///
/// Avoids issues with syncing build_runner_core to other repos.
class WebCompilationProxy {
  const WebCompilationProxy();

  /// Initialize the web compiler from the `projectDirectory`.
  ///
  /// Returns whether or not the build was successful.
  ///
  /// `release` controls whether we build the bundle for dartdevc or only
  /// the entrypoints for dart2js to later take over.
  Future<bool> initialize({
    @required Directory projectDirectory,
    @required String projectName,
    String testOutputDir,
    BuildMode mode,
    bool initializePlatform,
  }) async {
    throw UnimplementedError();
  }
}