// ignore_for_file: depend_on_referenced_packages

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

class FileRead extends Command {
  @override
  Future execute(DataCommand command) async {
    var data = DataFileRead.fromJson(getParam(command));
    var file = File(data.path);
    var content = '';
    if (file.existsSync()) {
      content = file.readAsStringSync();
    } else {
      throw Exception('File not exist - ${data.path}');
    }
    setProperty(data.setContent, content, showPrint: data.showPrint);
    return complete(0, command);
  }
}

class FileWrite extends Command {
  @override
  Future execute(DataCommand command) async {
    var data = DataFileWrite.fromJson(getParam(command));
    var file = File(data.path);
    if (file.existsSync()) {
      if (data.backup ?? false) {
        var now = DateTime.now();
        var postfix =
            '_${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}${now.hour.toString().padLeft(2, '0')}${now.minute.toString().padLeft(2, '0')}${now.second.toString().padLeft(2, '0')}${now.millisecond}';
        var fileName = path.basenameWithoutExtension(data.path);
        var fileExt = path.extension(data.path);
        var backupPath =
            path.join(path.dirname(data.path), fileName + postfix + fileExt);
        await file.copy(backupPath);
      }
      file.deleteSync();
    }

    await file.writeAsString(data.content);
    return complete(0, command);
  }
}

class Files extends Command {
  @override
  Future execute(DataCommand command) async {
    var data = DataFiles.fromJson(getParam(command));
    var target = Directory(data.path);
    var list = <String>[];
    var recursive = data.recursive ?? false;
    var regExs = data.ignoreRegExs ?? [];

    if (target.existsSync()) {
      var files = target.listSync(recursive: recursive);
      for (var file in files) {
        var path = file.path;
        var matched = false;
        for (var regEx in regExs) {
          var pattern = RegExp(regEx);
          if (pattern.hasMatch(path)) {
            matched = true;
            break;
          }
        }
        if (!matched) {
          if (FileSystemEntity.typeSync(path) == FileSystemEntityType.file) {
            list.add(path);
          }
        }
      }
    }

    await setProperty(data.setFiles, list, showPrint: data.showPrint);
    return complete(0, command);
  }
}

class FileInfo extends Command {
  @override
  Future execute(DataCommand command) async {
    var data = DataFileInfo.fromJson(getParam(command));
    var file = File(data.path);
    if (file.existsSync()) {
      var stat = await file.stat();
      var map = <String, dynamic>{};
      map['path'] = file.path;
      map['name'] = path.basename(file.path);
      map['withoutExtentionName'] = path.withoutExtension(map['name']);
      map['extension'] = path.extension(file.path);
      map['symbolicPath'] = await file.resolveSymbolicLinks();
      map['size'] = stat.size;
      map['accessed'] = stat.accessed.toString();
      map['changed'] = stat.changed.toString();
      map['modified'] = stat.modified.toString();
      map['type'] = stat.type.toString();
      map['mode'] = stat.modeString();
      setProperty(data.setInfo, map);
      return complete(0, command);
    } else {
      return complete(-1, command);
    }
  }
}

void registerFileCommands() {
  Command.register(
    CommandType.FileRead,
    () => FileRead(),
    spec: const CommandSpec(
      type: CommandType.FileRead,
      category: 'file',
      dataModel: 'DataFileRead',
      samplePath: 'assets/yaml/sample/07_file.yaml',
    ),
  );
  Command.register(
    CommandType.FileWrite,
    () => FileWrite(),
    spec: const CommandSpec(
      type: CommandType.FileWrite,
      category: 'file',
      dataModel: 'DataFileWrite',
      samplePath: 'assets/yaml/sample/07_file.yaml',
    ),
  );
  Command.register(
    CommandType.Files,
    () => Files(),
    spec: const CommandSpec(
      type: CommandType.Files,
      category: 'file',
      dataModel: 'DataFiles',
      samplePath: 'assets/yaml/sample/07_file_utils.yaml',
    ),
  );
  Command.register(
    CommandType.FileInfo,
    () => FileInfo(),
    spec: const CommandSpec(
      type: CommandType.FileInfo,
      category: 'file',
      dataModel: 'DataFileInfo',
      samplePath: 'assets/yaml/sample/07_file.yaml',
    ),
  );
}
