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

import 'package:dart_framework/utils/string_util.dart';
import 'package:oto/cli/cli.dart';
import 'package:oto/oto/commands/command.dart';
import 'package:oto/oto/data/command_data.dart';

class JsonBase extends Command {
  Future setValues(dynamic content, List<DataPair> pair) async {
    try {
      Map<String, dynamic> json = content is String
          ? jsonDecode(content)
          : content as Map<String, dynamic>;
      var arrReg = RegExp(r'^([a-zA-Z_]+)\[(\d+)\]$');
      for (var item in pair) {
        var temp = json;
        var keys = item.from.split('.');
        for (var i = 0; i < keys.length; ++i) {
          var key = keys[i];
          if (arrReg.hasMatch(key)) {
            //array case
            var match = arrReg.firstMatch(key);
            if (match != null && match.groupCount != 0) {
              var arrKey = match.group(1);
              var arrIndex = int.parse(match.group(2)!);
              if (temp.containsKey(arrKey)) {
                var arr = temp[arrKey] as List;
                if (arr.length > arrIndex) {
                  var value = arr[arrIndex];
                  if (i == keys.length - 1) {
                    await setProperty(item.setValue, value.toString());
                    break;
                  } else {
                    temp = value;
                  }
                } else {
                  throw Exception('Out of range: "$key" in $item');
                }
              } else {
                throw Exception('Not exist key: "$key" in $item');
              }
            } else {
              throw Exception('Not exist key: "$key" in $item');
            }
          } else {
            if (temp.containsKey(key)) {
              if (i == keys.length - 1) {
                await setProperty(item.setValue, temp[key].toString());
                break;
              } else {
                temp = temp[keys[i]];
              }
            } else {
              throw Exception('Not exist key: "$key" in $item');
            }
          }
        }
      }
    } on Exception catch (e) {
      throw Exception('Json parsing error: $e');
    }
  }
}

class JsonReader extends JsonBase {
  @override
  Future execute(DataCommand command) async {
    var data = DataJsonReader.fromJson(getParam(command));
    await setValues(data.json, data.pair);
    return complete(0, command);
  }
}

class JsonReaderFile extends JsonBase {
  @override
  Future execute(DataCommand command) async {
    var data = DataJsonReaderFile.fromJson(getParam(command));
    var path = Command.getAbs(data.path);
    var file = File(path);
    if (file.existsSync()) {
      await setValues(await file.readAsString(), data.pair);
    } else {
      await CLI.println('Json "$path" file not exit. Return value to be null.',
          color: Color.red);
    }
    return complete(0, command);
  }
}

class JsonWriterFile extends Command {
  @override
  Future execute(DataCommand command) async {
    var data = DataJsonWriterFile.fromJson(getParam(command));
    var path = Command.getAbs(data.path);
    var file = File(path);
    if (file.existsSync()) {
      var content = await file.readAsString();
      try {
        Map<String, dynamic> json = jsonDecode(content);
        for (var item in data.pair) {
          var temp = json;
          var keys = item.from.split('.');
          for (var i = 0; i < keys.length; ++i) {
            var key = keys[i];
            if (temp.containsKey(key)) {
              if (i == keys.length - 1) {
                temp[keys[i]] = item.value;
                break;
              } else {
                temp = temp[keys[i]];
              }
            } else {
              if (i == keys.length - 1) {
                temp[keys[i]] = item.value;
              } else {
                temp[keys[i]] = <String, dynamic>{};
                temp = temp[keys[i]];
              }
            }
          }
        }
        content = jsonPrettyEncode(json);
        file.writeAsStringSync(content, flush: true);
      } on Exception catch (e) {
        throw Exception('Json parsing error: $e');
      }
    } else {
      await CLI.println('Json "$path" file not exit. Return value to be null.',
          color: Color.red);
    }
    return complete(0, command);
  }
}

void registerJsonCommands() {
  Command.register(
    CommandType.JsonReader,
    () => JsonReader(),
    spec: const CommandSpec(
      type: CommandType.JsonReader,
      category: 'util',
      dataModel: 'DataJsonReader',
      samplePath: 'assets/yaml/sample/13_string_json.yaml',
    ),
  );
  Command.register(
    CommandType.JsonReaderFile,
    () => JsonReaderFile(),
    spec: const CommandSpec(
      type: CommandType.JsonReaderFile,
      category: 'util',
      dataModel: 'DataJsonReaderFile',
      samplePath: 'assets/yaml/sample/13_string_json.yaml',
    ),
  );
  Command.register(
    CommandType.JsonWriterFile,
    () => JsonWriterFile(),
    spec: const CommandSpec(
      type: CommandType.JsonWriterFile,
      category: 'util',
      dataModel: 'DataJsonWriterFile',
      samplePath: 'assets/yaml/sample/13_string_json.yaml',
    ),
  );
}
