import 'dart:async';
import 'dart:io';

import 'package:path/path.dart' as path;
import 'package:oto/oto/application.dart';
import 'package:oto/oto/commands/command.dart';
import 'package:oto/oto/data/command_data.dart';

class CodeSign extends Command {
  Function? get error => null;

  @override
  Future execute(DataCommand command) async {
    var data = DataCodeSign.fromJson(getParam(command));
    var entitlement = '';
    if (data.entitlementPath != null) {
      entitlement = ' --entitlements "${data.entitlementPath}"';
    }
    var shell = StringBuffer(getCDPath(workspace));
    shell.write(
        ' && codesign --deep --force --verify --verbose --timestamp --options runtime$entitlement --sign "${data.certificationName}" "${data.appPath}"');

    var process = await runtime.start(shell,
        workspace: workspace,
        printStderr: false,
        logHandler: Application.logWithType);

    return await completeProcess(process, command);
  }
}

class CodeSignVerify extends Command {
  Function? get error => null;

  @override
  Future execute(DataCommand command) async {
    var data = DataCodeSignVerify.fromJson(getParam(command));
    var shell = StringBuffer(getCDPath(workspace));
    shell.write(' && codesign -verify -verbose "${data.appPath}"');
    shell.write(' && spctl --assess --verbose "${data.appPath}"');

    var process = await runtime.start(shell,
        workspace: workspace,
        printStderr: false,
        logHandler: Application.logWithType);

    return await completeProcess(process, command);
  }
}

class ProductBuild extends Command {
  Function? get error => null;

  @override
  Future execute(DataCommand command) async {
    var data = DataProductBuild.fromJson(getParam(command));
    var shell = StringBuffer(getCDPath(workspace));
    if (data.scripts != null) {
      shell.write(' && chmod a+x "${data.scripts}/postinstall"');
      shell.write(
          ' && pkgbuild --identifier "${data.identifier}" --scripts "${data.scripts}" --component "${data.appPath}" --sign "${data.installCertificationName}" --install-location "/Applications" "${data.unsignedResultPath}"');
    } else {
      shell.write(
          ' && productbuild --component "${data.appPath}" /Applications --sign "${data.installCertificationName}" "${data.unsignedResultPath}"');
    }
    shell.write(
        ' && productsign --sign "${data.installCertificationName}" "${data.unsignedResultPath}" "${data.signedResultPath}"');

    var process = await runtime.start(shell,
        workspace: workspace,
        printStderr: false,
        logHandler: Application.logWithType);

    return await completeProcess(process, command);
  }
}

class Notarize extends Command {
  Function? get error => null;

  @override
  Future execute(DataCommand command) async {
    var data = DataNotarize.fromJson(getParam(command));
    var isApp = path.extension(data.resultPath).toLowerCase().contains('app');
    var shell = StringBuffer(getCDPath(workspace));
    shell.write(' && security unlock-keychain -p${data.macPassword}');
    shell.write(
        ' && xcrun notarytool store-credentials "${data.bundleName}" --apple-id "${data.appleAccount}" --team-id "${data.teamId}" --password "${data.applePassword}"');
    var notaryTarget = data.resultPath;
    var appFileName = '';
    var zipFileName = '';
    if (isApp) {
      var dir = Directory(data.resultPath).parent;
      appFileName = path.basename(data.resultPath);
      zipFileName = '${path.basenameWithoutExtension(data.resultPath)}.zip';
      notaryTarget = '${dir.path}/$zipFileName';
      shell.write(' && cd ${dir.path}');
      shell.write(
          ' && ditto -c -k --sequesterRsrc --keepParent "$appFileName" "$zipFileName"');
    }
    shell.write(
        ' && xcrun notarytool submit "$notaryTarget" --keychain-profile "${data.bundleName}" --wait');
    shell.write(' && xcrun stapler staple "${data.resultPath}"');
    if (isApp) {
      shell.write(' && rm $zipFileName');
      shell.write(
          ' && ditto -c -k --sequesterRsrc --keepParent "$appFileName" "$zipFileName"');
    }

    var process = await runtime.start(shell,
        workspace: workspace,
        printStderr: false,
        logHandler: Application.logWithType);

    return await completeProcess(process, command);
  }
}
