// ignore_for_file: depend_on_referenced_packages

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';

import 'package:dart_framework/utils/system_util.dart';
import 'package:path/path.dart' as path;

import 'cli_style.dart';
import '../oto/core/system_runtime.dart';

final _colorCodeMap = {
  Color.black: "30",
  Color.red: "31",
  Color.green: "32",
  Color.yellow: "33",
  Color.blue: "34",
  Color.magenta: "35",
  Color.cyan: "36",
  Color.lightGray: "37",
  Color.darkGray: "90",
  Color.redStrong: "91",
  Color.greenStrong: "92",
  Color.yellowStrong: "93",
  Color.blueStrong: "94",
  Color.magentaStrong: "95",
  Color.cyanStrong: "96",
  Color.white: "97"
};

final _backgroundCodeMap = {
  Color.black: "40",
  Color.red: "41",
  Color.green: "42",
  Color.yellow: "43",
  Color.blue: "44",
  Color.magenta: "45",
  Color.cyan: "46",
  Color.lightGray: "47",
  Color.darkGray: "100",
  Color.redStrong: "101",
  Color.greenStrong: "102",
  Color.yellowStrong: "103",
  Color.blueStrong: "104",
  Color.magentaStrong: "105",
  Color.cyanStrong: "106",
  Color.white: "107"
};

final _styleCodeMap = {
  Style.bold: "1",
  Style.dim: "2",
  Style.underline: "4",
  Style.reverse: "7"
};

abstract class Printer {
  Printer();
  factory Printer.windows(
          {SystemRuntime runtime = const DefaultSystemRuntime()}) =>
      _PrinterWindows(runtime);
  factory Printer.unix(
          {SystemRuntime runtime = const DefaultSystemRuntime()}) =>
      _PrinterUnix(runtime);

  String getStyle(String message,
      {Color? color, Color? background, Style? style}) {
    var combine = '';
    if (color != null) {
      combine = _colorCodeMap[color]!;
    }
    if (background != null) {
      combine = combineText(combine, _backgroundCodeMap[background]!);
    }
    if (style != null) {
      combine = combineText(combine, _styleCodeMap[style]!);
    }
    return combine;
  }

  String combineText(String combine, String item) {
    if (combine == '') {
      combine = item;
    } else {
      combine += ';$item';
    }
    return combine;
  }

  Future<File> getTempFile(String content, String extension) async {
    var now = DateTime.now();
    var time =
        '${now.hour}${now.minute}${now.second}${now.millisecond}${now.microsecond}';
    var tempFile = File(path.join(Directory.systemTemp.path,
        'temp_${time}_${Random().nextInt(1000)}.$extension'));
    await tempFile.writeAsString(content);
    return dataFutrue(tempFile);
  }

  Future printCLI(List<String> printList) async {}
}

class _PrinterWindows extends Printer {
  final SystemRuntime runtime;
  _PrinterWindows(this.runtime);

  @override
  String getStyle(String message,
      {Color? color, Color? background, Style? style}) {
    var combine = super
        .getStyle(message, color: color, background: background, style: style);
    return '%ESC%[${combine}m$message%ESC%[0m';
  }

  String removeTags(String value) {
    return value
        .replaceAll('^', '^^')
        .replaceAll('<', '^<')
        .replaceAll('>', '^>')
        .replaceAll('[', '^[')
        .replaceAll('╷', '^╷')
        .replaceAll('│', '^│')
        .replaceAll('╵', '^╵')
        .replaceAll(']', '^]')
        .replaceAll('|', '^|')
        .replaceAll('\r', '')
        .replaceAll('\n', '');
  }

  @override
  Future printCLI(List<String> printList) async {
    StringBuffer message = StringBuffer();
    message.writeln('@echo off\r');
    message.writeln('chcp 65001 > nul');
    message.writeln('setlocal\r');
    message.writeln('call :setESC\r');

    if (printList.length == 1) {
      message.writeln('echo | set /p=${removeTags(printList.first)}');
    } else {
      for (var item in printList) {
        item = removeTags(item);
        if (item.trim().isEmpty) {
          message.writeln('echo.\r');
        } else {
          message.writeln('echo $item\r');
        }
      }
    }
    message.writeln(':setESC\r');
    message.writeln(
        '''for /F "tokens=1,2 delims=#" %%a in ('"prompt #\$H#\$E# & echo on & for %%b in (1) do rem"') do (\r''');
    message.writeln('  set ESC=%%b\r');
    message.writeln('  exit /B 0\r');
    message.writeln(')\r');
    message.writeln('exit /B 0\r');

    var temp = await getTempFile(message.toString(), 'bat');
    await runtime
        .runExecutable('cmd', ['/C', temp.path],
            stderrEncoding: utf8, stdoutEncoding: utf8)
        .then((ProcessResult result) {
      print(result.stdout);

      String error = result.stderr;
      if (error.isNotEmpty) {
        print(
            '=============== Error ===============\r\n${result.stderr}\r\n\r\nr\n=============== Script ===============\r\n${message.toString()}');
      }
    });
    await temp.delete();
    return simpleFuture;
  }
}

class _PrinterUnix extends Printer {
  final SystemRuntime runtime;
  _PrinterUnix(this.runtime);

  @override
  String getStyle(String message,
      {Color? color, Color? background, Style? style}) {
    var combine = super
        .getStyle(message, color: color, background: background, style: style);
    return '\\e[${combine}m$message\\e[0m';
  }

  @override
  Future printCLI(List<String> printList) async {
    StringBuffer message = StringBuffer();
    message.write('echo -e "');

    for (var item in printList) {
      message.writeln(item);
    }
    message.write('"');

    var temp = await getTempFile(message.toString(), 'sh');
    var shellExe = runtime.isMacOS ? 'zsh' : 'bash';
    await runtime
        .runExecutable(
      shellExe,
      [temp.path],
      stdoutEncoding: utf8,
      stderrEncoding: utf8,
    )
        .then((ProcessResult result) {
      print(result.stdout);
    });
    await temp.delete();
    return simpleFuture;
  }
}
