import 'dart:convert';
import 'dart:io';

import 'package:dart_framework/utils/system_util.dart';
import 'package:oto/cli/cli.dart';
import 'package:oto/oto/application.dart';
import 'package:oto/oto/data/base_data.dart';
import 'package:oto/oto/core/defined_data.dart';
import 'package:oto/oto/core/system_runtime.dart';

abstract class DataComposer {
  DataCommon? commonData;
  String buildYaml = '';

  Future compose(String? yaml, Future Function(String, Color?) printer) async {
    return simpleFuture;
  }
}

class DataComposerTest extends DataComposer {
  @override
  Future compose(String? yaml, Future Function(String, Color?) printer) async {
    commonData = DataCommon.fromJson(TestData.jenkinsMap!);
    buildYaml = TestData.buildYaml;
    return simpleFuture;
  }
}

class DataComposerFile extends DataComposer {
  @override
  Future compose(String? yaml, Future Function(String, Color?) printer) async {
    commonData = DataCommon.fromJson(LocalData.commonMap());
    buildYaml = yaml ?? '---';
    return simpleFuture;
  }
}

class JenkinsEnvironmentSnapshot {
  final Map<String, dynamic> variables;
  final String buildYaml;
  final String rawVariablesJson;

  const JenkinsEnvironmentSnapshot({
    required this.variables,
    required this.buildYaml,
    required this.rawVariablesJson,
  });
}

class JenkinsEnvironmentCollector {
  final SystemRuntime runtime;

  JenkinsEnvironmentCollector({required this.runtime});

  Future<JenkinsEnvironmentSnapshot?> collect(
      Future Function(String, Color?) printer) async {
    if (runtime.isMacOS || runtime.isLinux) {
      return _collectUnix();
    }
    if (runtime.isWindows) {
      return _collectWindows();
    }
    await printer(
        'is not Mac/Windows/Linux. build must be execute in Mac/Windows os system',
        Color.redStrong);
    return null;
  }

  Future<JenkinsEnvironmentSnapshot> _collectUnix() async {
    var script = '''
# shellcheck disable=SC2016
echo -e '
{
  "workspace":"'\${WORKSPACE}'",
  "jenkinsHome":"'\${JENKINS_HOME}'",
  "jenkinsUrl":"'\${JENKINS_URL}'",
  "buildUrl":"'\${BUILD_URL}'",
  "jobUrl":"'\${JOB_URL}'",
  "jobName":"'\${JOB_NAME}'",
  "gitCommit":"'\${GIT_COMMIT}'",
  "buildNumber":'\${BUILD_NUMBER}',
  "buildID":'\${BUILD_ID}',
  "buildDisplayName":"'\${BUILD_DISPLAY_NAME}'",
  "gitBranch":"'\${GIT_BRANCH}'",
  "buildTag":"'\${BUILD_TAG}'"
}'
''';
    final result = await runtime.runShell(StringBuffer(script));
    final rawJson = result.stdout.toString();
    final variables = getMapFromJson(rawJson)!;

    var shell = StringBuffer('printf "%s\n" "\${BuildData}"');
    var process = await runtime.startShell(shell, printStdout: false);
    await process.waitForExit();
    final buildYaml = process.stdout.toString();

    return JenkinsEnvironmentSnapshot(
      variables: variables,
      buildYaml: buildYaml,
      rawVariablesJson: rawJson,
    );
  }

  Future<JenkinsEnvironmentSnapshot> _collectWindows() async {
    String jsonStr = '';
    await runtime.runExecutable('cmd', [
      '/C',
      '${Directory.current.path}/assets/script/batch/jenkins_env_params.bat'
    ]).then((ProcessResult result) => jsonStr = result.stdout);
    jsonStr = jsonStr.substring(jsonStr.indexOf('{'));
    jsonStr = jsonStr.replaceAll('\\', '/');
    final variables = getMapFromJson(jsonStr)!;
    final workspace = variables['workspace'] as String;
    final buildYaml = await _getBuildDataWindows(workspace);

    return JenkinsEnvironmentSnapshot(
      variables: variables,
      buildYaml: buildYaml,
      rawVariablesJson: jsonStr,
    );
  }

  Future<String> _getBuildDataWindows(String workspace) async {
    var file = File('$workspace/build_data.conf');
    var content = file.readAsStringSync(encoding: utf8).replaceAll('\\', '/');
    var index = content.indexOf('=');
    content = content.substring(index + 1);
    var regEx = RegExp(r'(%)([a-zA-Z0-9]+)(%)');
    var list = regEx.allMatches(content);
    var params = <String>[];
    for (var match in list) {
      var param = match.group(2);
      if (!params.contains(param)) {
        params.add(param!);
      }
    }

    var temp = File('$workspace/param_temp.bat');
    var bat = '';
    for (var param in params) {
      bat += '@echo %$param%\n';
    }
    temp.writeAsStringSync(bat);

    var resultParam = '';
    await runtime
        .runExecutable('cmd', ['/C', temp.path])
        .then((ProcessResult result) => resultParam = result.stdout);
    var values = resultParam.split('\n');
    var length = params.length;
    for (var i = 0; i < length; ++i) {
      var value = values[i]
          .toString()
          .replaceAll('\\', '/')
          .replaceAll(RegExp('\r|\n'), '');
      content = content.replaceAll('%${params[i]}%', value);
    }
    temp.deleteSync();
    return content;
  }
}

class DataComposerJenkins extends DataComposer {
  final SystemRuntime runtime;

  DataComposerJenkins({required this.runtime});

  @override
  Future compose(String? yaml, Future Function(String, Color?) printer) async {
    Application.log('Current OS: ${Platform.operatingSystem}');
    Application.log('Start build...');

    final collector = JenkinsEnvironmentCollector(runtime: runtime);
    final snapshot = await collector.collect(printer);
    if (snapshot == null) return simpleFuture;

    commonData = DataCommon.fromJson(snapshot.variables);
    buildYaml = snapshot.buildYaml;
    await printer('Jenkins Variables', Color.green);
    Application.log(snapshot.rawVariablesJson);

    return simpleFuture;
  }
}
