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

import 'package:fluent_ui/fluent_ui.dart' show debugPrint;
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:intl/intl.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
import 'package:starcitizen_doctor/generated/l10n.dart';
import 'package:watcher/watcher.dart';

part 'log_analyze_provider.g.dart';

part 'log_analyze_provider.freezed.dart';

final Map<String?, String> logAnalyzeSearchTypeMap = {
  null: S.current.log_analyzer_filter_all,
  "info": S.current.log_analyzer_filter_basic_info,
  "player_login": S.current.log_analyzer_filter_account_related,
  "fatal_collision": S.current.log_analyzer_filter_fatal_collision,
  "vehicle_destruction": S.current.log_analyzer_filter_vehicle_damaged,
  "actor_death": S.current.log_analyzer_filter_character_death,
  "statistics": S.current.log_analyzer_filter_statistics,
  "game_crash": S.current.log_analyzer_filter_game_crash,
  "request_location_inventory": S.current.log_analyzer_filter_local_inventory,
};

@freezed
abstract class LogAnalyzeLineData with _$LogAnalyzeLineData {
  const factory LogAnalyzeLineData({
    required String type,
    required String title,
    String? data,
    String? dateTime,
  }) = _LogAnalyzeLineData;
}

@riverpod
class ToolsLogAnalyze extends _$ToolsLogAnalyze {
  static const String unknownValue = "<Unknown>";

  @override
  Future<List<LogAnalyzeLineData>> build(String gameInstallPath, bool listSortReverse) async {
    final logFile = File("$gameInstallPath/Game.log");
    debugPrint("[ToolsLogAnalyze] logFile: ${logFile.absolute.path}");
    if (gameInstallPath.isEmpty || !(await logFile.exists())) {
      return [
        LogAnalyzeLineData(
          type: "error",
          title: S.current.log_analyzer_no_log_file,
        )
      ];
    }
    state = AsyncData([]);
    _launchLogAnalyze(logFile);
    return state.value ?? [];
  }

  String _playerName = ""; // 记录玩家名称
  int _killCount = 0; // 记录击杀其他实体次数
  int _deathCount = 0; // 记录被击杀次数
  int _selfKillCount = 0; // 记录自杀次数
  int _vehicleDestructionCount = 0; // 记录载具损毁次数 （软死亡）
  int _vehicleDestructionCountHard = 0; // 记录载具损毁次数 （解体）
  DateTime? _gameStartTime; // 记录游戏开始时间
  int _gameCrashLineNumber = -1; // 记录${S.current.log_analyzer_filter_game_crash}行号
  int _currentLineNumber = 0; // 当前行号

  void _launchLogAnalyze(File logFile, {int startLine = 0}) async {
    final logLines = utf8.decode((await logFile.readAsBytes()), allowMalformed: true).split("\n");
    debugPrint("[ToolsLogAnalyze] logLines: ${logLines.length}");
    if (startLine == 0) {
      _killCount = 0;
      _deathCount = 0;
      _selfKillCount = 0;
      _vehicleDestructionCount = 0;
      _vehicleDestructionCountHard = 0;
      _gameStartTime = null;
      _gameCrashLineNumber = -1;
    } else if (startLine > logLines.length) {
      // 考虑文件重新写入的情况
      ref.invalidateSelf();
    }
    _currentLineNumber = logLines.length;
    // for i in logLines
    for (var i = 0; i < logLines.length; i++) {
      // 支持追加模式
      if (i < startLine) continue;
      final line = logLines[i];
      if (line.isEmpty) continue;
      final data = _handleLogLine(line, i);
      if (data != null) {
        _appendResult(data);
        // wait for ui update
        await Future.delayed(Duration(seconds: 0));
      }
    }

    final lastLineDateTime =
        _gameStartTime != null ? _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20"))) : null;

    // 检查${S.current.log_analyzer_filter_game_crash}行号
    if (_gameCrashLineNumber > 0) {
      // crashInfo 从 logLines _gameCrashLineNumber 开始到最后一行
      final crashInfo = logLines.sublist(_gameCrashLineNumber);
      // 运行一键诊断
      final info = SCLoggerHelper.getGameRunningLogInfo(crashInfo);
      crashInfo.add(S.current.log_analyzer_one_click_diagnosis_header);
      if (info != null) {
        crashInfo.add(info.key);
        if (info.value.isNotEmpty) {
          crashInfo.add(S.current.log_analyzer_details_info(info.value));
        }
      } else {
        crashInfo.add(S.current.log_analyzer_no_crash_detected);
      }
      _appendResult(LogAnalyzeLineData(
        type: "game_crash",
        title: S.current.log_analyzer_game_crash,
        data: crashInfo.join("\n"),
        dateTime: lastLineDateTime != null ? _dateTimeFormatter.format(lastLineDateTime) : null,
      ));
    }

    // ${S.current.log_analyzer_kill_summary}
    if (_killCount > 0 || _deathCount > 0) {
      _appendResult(LogAnalyzeLineData(
        type: "statistics",
        title: S.current.log_analyzer_kill_summary,
        data: S.current.log_analyzer_kill_death_suicide_count(
          _killCount,
          _deathCount,
          _selfKillCount,
          _vehicleDestructionCount,
          _vehicleDestructionCountHard,
        ),
      ));
    }

    // 统计${S.current.log_analyzer_play_time}，_gameStartTime 减去 最后一行的时间
    if (_gameStartTime != null) {
      if (lastLineDateTime != null) {
        final duration = lastLineDateTime.difference(_gameStartTime!);
        _appendResult(LogAnalyzeLineData(
          type: "statistics",
          title: S.current.log_analyzer_play_time,
          data: S.current.log_analyzer_play_time_format(
            duration.inHours,
            duration.inMinutes.remainder(60),
            duration.inSeconds.remainder(60),
          ),
        ));
      }
    }

    _startListenFile(logFile);
  }

  // 避免重复调用
  bool _isListenEnabled = false;

  Future<void> _startListenFile(File logFile) async {
    _isListenEnabled = true;
    debugPrint("[ToolsLogAnalyze] startListenFile: ${logFile.absolute.path}");
    // 监听文件
    late final StreamSubscription sub;
    sub = FileWatcher(logFile.absolute.path, pollingDelay: Duration(seconds: 1)).events.listen((change) {
      sub.cancel();
      if (!_isListenEnabled) return;
      _isListenEnabled = false;
      debugPrint("[ToolsLogAnalyze] logFile change: ${change.type}");
      switch (change.type) {
        case ChangeType.MODIFY:
          // 移除${S.current.log_analyzer_filter_statistics}
          final newList = state.value?.where((e) => e.type != "statistics").toList();
          if (listSortReverse) {
            state = AsyncData(newList?.reversed.toList() ?? []);
          } else {
            state = AsyncData(newList ?? []);
          }
          return _launchLogAnalyze(logFile, startLine: _currentLineNumber);
        case ChangeType.ADD:
        case ChangeType.REMOVE:
          ref.invalidateSelf();
          return;
      }
    });
    ref.onDispose(() {
      sub.cancel();
    });
  }

  LogAnalyzeLineData? _handleLogLine(String line, int index) {
    // 处理 log 行，检测可以提取的内容
    if (_gameStartTime == null) {
      _gameStartTime = _getLogLineDateTime(line);
      return LogAnalyzeLineData(
        type: "info",
        title: S.current.log_analyzer_game_start,
        dateTime: _getLogLineDateTimeString(line),
      );
    }
    // 读取${S.current.log_analyzer_game_loading}时间
    final gameLoading = _logGetGameLoading(line);
    if (gameLoading != null) {
      return LogAnalyzeLineData(
        type: "info",
        title: S.current.log_analyzer_game_loading,
        data: S.current.log_analyzer_mode_loading_time(
          gameLoading.$1,
          gameLoading.$2,
        ),
        dateTime: _getLogLineDateTimeString(line),
      );
    }

    // 运行基础时间解析器
    final baseEvent = _baseEventDecoder(line);
    if (baseEvent != null) {
      switch (baseEvent) {
        case "AccountLoginCharacterStatus_Character":
          // 角色登录
          return _logGetCharacterName(line);
        case "FatalCollision":
          // 载具${S.current.log_analyzer_filter_fatal_collision}
          return _logGetFatalCollision(line);
        case "Vehicle Destruction":
          // ${S.current.log_analyzer_filter_vehicle_damaged}
          return _logGetVehicleDestruction(line);
        case "Actor Death":
          // ${S.current.log_analyzer_filter_character_death}
          return _logGetActorDeath(line);
        case "RequestLocationInventory":
          // 请求${S.current.log_analyzer_filter_local_inventory}
          return _logGetRequestLocationInventory(line);
      }
    }

    if (line.contains("[CIG] CCIGBroker::FastShutdown")) {
      return LogAnalyzeLineData(
        type: "info",
        title: S.current.log_analyzer_game_close,
        dateTime: _getLogLineDateTimeString(line),
      );
    }

    if (line.contains("Cloud Imperium Games public crash handler")) {
      _gameCrashLineNumber = index;
    }

    return null;
  }

  void _appendResult(LogAnalyzeLineData data) {
    // 追加结果到 state
    final currentState = state.value;
    if (currentState != null) {
      if (listSortReverse) {
        // 反向排序
        state = AsyncData([data, ...currentState]);
      } else {
        state = AsyncData([...currentState, data]);
      }
    } else {
      state = AsyncData([data]);
    }
  }

  final _baseRegExp = RegExp(r'\[Notice\]\s+<([^>]+)>');

  String? _baseEventDecoder(String line) {
    // 解析 log 行的基本信息
    final match = _baseRegExp.firstMatch(line);
    if (match != null) {
      final type = match.group(1);
      return type;
    }
    return null;
  }

  final _gameLoadingRegExp =
      RegExp(r'<[^>]+>\s+Loading screen for\s+(\w+)\s+:\s+SC_Frontend closed after\s+(\d+\.\d+)\s+seconds');

  (String, String)? _logGetGameLoading(String line) {
    final match = _gameLoadingRegExp.firstMatch(line);
    if (match != null) {
      return (match.group(1) ?? "-", match.group(2) ?? "-");
    }
    return null;
  }

  final DateFormat _dateTimeFormatter = DateFormat('yyyy-MM-dd HH:mm:ss:SSS');
  final _logDateTimeRegExp = RegExp(r'<(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)>');

  DateTime? _getLogLineDateTime(String line) {
    // 提取 log 行的时间
    final match = _logDateTimeRegExp.firstMatch(line);
    if (match != null) {
      final dateTimeString = match.group(1);
      if (dateTimeString != null) {
        return DateTime.parse(dateTimeString).toLocal();
      }
    }
    return null;
  }

  String? _getLogLineDateTimeString(String line) {
    // 提取 log 行的时间
    final dateTime = _getLogLineDateTime(line);
    if (dateTime != null) {
      return _dateTimeFormatter.format(dateTime);
    }
    return null;
  }

  // 安全提取函数
  String? safeExtract(RegExp pattern, String line) => pattern.firstMatch(line)?.group(1)?.trim();

  LogAnalyzeLineData? _logGetFatalCollision(String line) {
    final patterns = {
      'zone': RegExp(r'\[Part:[^\]]*?Zone:\s*([^,\]]+)'),
      'player_pilot': RegExp(r'PlayerPilot:\s*(\d)'),
      'hit_entity': RegExp(r'hitting entity:\s*(\w+)'),
      'hit_entity_vehicle': RegExp(r'hitting entity:[^\[]*\[Zone:\s*([^\s-]+)'),
      'distance': RegExp(r'Distance:\s*([\d.]+)')
    };

    final zone = safeExtract(patterns['zone']!, line) ?? unknownValue;
    final playerPilot = (safeExtract(patterns['player_pilot']!, line) ?? '0') == '1';
    final hitEntity = safeExtract(patterns['hit_entity']!, line) ?? unknownValue;
    final hitEntityVehicle = safeExtract(patterns['hit_entity_vehicle']!, line) ?? unknownValue;
    final distance = double.tryParse(safeExtract(patterns['distance']!, line) ?? '') ?? 0.0;
    return LogAnalyzeLineData(
      type: "fatal_collision",
      title: S.current.log_analyzer_filter_fatal_collision,
      data: S.current.log_analyzer_collision_details(
        zone,
        playerPilot ? '✅' : '❌',
        hitEntity,
        hitEntityVehicle,
        distance.toStringAsFixed(2),
      ),
      dateTime: _getLogLineDateTimeString(line),
    );
  }

  LogAnalyzeLineData? _logGetVehicleDestruction(String line) {
    final pattern = RegExp(r"Vehicle\s+'([^']+)'.*?" // 载具型号
        r"in zone\s+'([^']+)'.*?" // Zone
        r"destroy level \d+ to (\d+).*?" // 损毁等级
        r"caused by\s+'([^']+)'" // 责任方
        );
    final match = pattern.firstMatch(line);
    if (match != null) {
      final vehicleModel = match.group(1) ?? unknownValue;
      final zone = match.group(2) ?? unknownValue;
      final destructionLevel = int.tryParse(match.group(3) ?? '') ?? 0;
      final causedBy = match.group(4) ?? unknownValue;

      final destructionLevelMap = {1: S.current.log_analyzer_soft_death, 2: S.current.log_analyzer_disintegration};

      if (causedBy.trim() == _playerName) {
        if (destructionLevel == 1) {
          _vehicleDestructionCount++;
        } else if (destructionLevel == 2) {
          _vehicleDestructionCountHard++;
        }
      }

      return LogAnalyzeLineData(
        type: "vehicle_destruction",
        title: S.current.log_analyzer_filter_vehicle_damaged,
        data: S.current.log_analyzer_vehicle_damage_details(
          vehicleModel,
          zone,
          destructionLevel.toString(),
          destructionLevelMap[destructionLevel] ?? unknownValue,
          causedBy,
        ),
        dateTime: _getLogLineDateTimeString(line),
      );
    }
    return null;
  }

  LogAnalyzeLineData? _logGetActorDeath(String line) {
    final pattern = RegExp(r"CActor::Kill: '([^']+)'.*?" // 受害者ID
        r"in zone '([^']+)'.*?" // 死亡位置区域
        r"killed by '([^']+)'.*?" // 击杀者ID
        r"with damage type '([^']+)'" // 伤害类型
        );

    final match = pattern.firstMatch(line);
    if (match != null) {
      final victimId = match.group(1) ?? unknownValue;
      final zone = match.group(2) ?? unknownValue;
      final killerId = match.group(3) ?? unknownValue;
      final damageType = match.group(4) ?? unknownValue;

      if (victimId.trim() == killerId.trim()) {
        // 自杀
        _selfKillCount++;
      } else {
        if (victimId.trim() == _playerName) {
          _deathCount++;
        }
        if (killerId.trim() == _playerName) {
          _killCount++;
        }
      }

      return LogAnalyzeLineData(
        type: "actor_death",
        title: S.current.log_analyzer_filter_character_death,
        data: S.current.log_analyzer_death_details(
          victimId,
          damageType,
          killerId,
          zone,
        ),
        dateTime: _getLogLineDateTimeString(line),
      );
    }

    return null;
  }

  LogAnalyzeLineData? _logGetCharacterName(String line) {
    final pattern = RegExp(r"name\s+([^-]+)");
    final match = pattern.firstMatch(line);
    if (match != null) {
      final characterName = match.group(1)?.trim() ?? unknownValue;
      _playerName = characterName.trim(); // 更新玩家名称
      return LogAnalyzeLineData(
        type: "player_login",
        title: S.current.log_analyzer_player_login(characterName),
        dateTime: _getLogLineDateTimeString(line),
      );
    }
    return null;
  }

  LogAnalyzeLineData? _logGetRequestLocationInventory(String line) {
    final pattern = RegExp(r"Player\[([^\]]+)\].*?Location\[([^\]]+)\]");
    final match = pattern.firstMatch(line);
    if (match != null) {
      final playerId = match.group(1) ?? unknownValue;
      final location = match.group(2) ?? unknownValue;

      return LogAnalyzeLineData(
        type: "request_location_inventory",
        title: S.current.log_analyzer_view_local_inventory,
        data: S.current.log_analyzer_player_location(playerId, location),
        dateTime: _getLogLineDateTimeString(line),
      );
    }
    return null;
  }
}
