import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:logging/logging.dart';
import 'mining_task_info.dart';

/// 挖矿管理器 - 管理挖矿软件的启动、停止
class MiningManager {
  static final MiningManager _instance = MiningManager._internal();
  factory MiningManager() => _instance;
  MiningManager._internal();

  final Logger _logger = Logger('MiningManager');
  Process? _currentProcess;
  MiningTaskInfo? _currentTask;
  Timer? _taskMonitor;
  final StreamController<String> _minerLogController = StreamController<String>.broadcast();
  final StreamController<void> _processExitController = StreamController<void>.broadcast();
  
  /// 进程退出事件流
  Stream<void> get processExitStream => _processExitController.stream;

  /// 启动挖矿
  Future<bool> startMining(MiningTaskInfo task, MiningConfig config) async {
    if (_currentProcess != null) {
      _logger.warning('已有挖矿任务在运行');
      return false;
    }

    try {
      final minerPath = _getMinerPath(task.miner, config);
      if (minerPath == null) {
        _logger.severe('挖矿软件路径未配置: ${task.miner}');
        return false;
      }

      final args = _buildMinerArgs(task, config);
      String executable;
      
      switch (task.miner.toLowerCase()) {
        case 'lolminer':
          executable = Platform.isWindows 
              ? '${minerPath}\\lolMiner.exe'
              : '$minerPath/lolMiner';
          break;
        case 'rigel':
          executable = Platform.isWindows 
              ? '${minerPath}\\rigel.exe'
              : '$minerPath/rigel';
          break;
        case 'bzminer':
          executable = Platform.isWindows 
              ? '${minerPath}\\bzminer.exe'
              : '$minerPath/bzminer';
          break;
        default:
          throw Exception('不支持的挖矿软件: ${task.miner}');
      }
      
      // 确保可执行文件存在
      final executableFile = File(executable);
      if (!await executableFile.exists()) {
        throw Exception('挖矿软件不存在: $executable');
      }

      _logger.info('启动挖矿: $executable ${args.join(' ')}');

      _currentProcess = await Process.start(
        executable,
        args,
        workingDirectory: minerPath,
        mode: ProcessStartMode.normal, // 改为 normal 以捕获输出
      );

      _currentTask = task;
      _startTaskMonitor(task);
      _startLogCapture();
      _monitorProcessExit();

      _logger.info('挖矿已启动 (PID: ${_currentProcess!.pid})');
      return true;
    } catch (e) {
      _logger.severe('启动挖矿失败: $e');
      return false;
    }
  }

  /// 停止挖矿
  Future<void> stopMining() async {
    _taskMonitor?.cancel();
    _taskMonitor = null;

    if (_currentProcess != null) {
      try {
        _currentProcess!.kill();
        await _currentProcess!.exitCode;
        _logger.info('挖矿已停止');
      } catch (e) {
        _logger.severe('停止挖矿失败: $e');
      } finally {
        _currentProcess = null;
        _currentTask = null;
      }
    }
  }

  /// 获取挖矿软件路径
  String? _getMinerPath(String miner, MiningConfig config) {
    switch (miner.toLowerCase()) {
      case 'lolminer':
        return config.lolMinerPath;
      case 'rigel':
        return config.rigelPath;
      case 'bzminer':
        return config.bzMinerPath;
      default:
        return null;
    }
  }

  /// 构建挖矿软件参数
  List<String> _buildMinerArgs(MiningTaskInfo task, MiningConfig config) {
    final address = task.walletMining ? task.walletAddress : (task.poolUser ?? task.walletAddress);
    
    switch (task.miner.toLowerCase()) {
      case 'lolminer':
        return [
          '--algo', task.coin,
          '--pool', task.poolUrl,
          '--user', '$address.${task.workerId}',
        ];
      case 'rigel':
        return [
          '--no-watchdog',
          '-a', task.algo.toLowerCase(),
          '-o', task.poolUrl,
          '-u', address,
          '-w', task.workerId,
          '--log-file', 'logs/miner.log',
        ];
      case 'bzminer':
        return [
          '-a', task.coin,
          '-w', '$address.${task.workerId}',
          '-p', task.poolUrl,
        ];
      default:
        return [];
    }
  }

  /// 启动任务监控
  void _startTaskMonitor(MiningTaskInfo task) {
    _taskMonitor?.cancel();
    
    final endTime = DateTime.fromMillisecondsSinceEpoch(task.endTimestamp * 1000);
    final now = DateTime.now();
    
    if (endTime.isBefore(now)) {
      // 任务已过期，立即停止
      stopMining();
      return;
    }

    final duration = endTime.difference(now);
    _taskMonitor = Timer(duration, () {
      _logger.info('挖矿任务已到期，自动停止');
      stopMining();
    });
  }

  /// 开始捕获挖矿进程输出
  void _startLogCapture() {
    if (_currentProcess == null) return;
    
    _currentProcess!.stdout
        .transform(const SystemEncoding().decoder)
        .transform(const LineSplitter())
        .listen((line) {
      _minerLogController.add('[${DateTime.now().toString().substring(11, 19)}] $line');
    });
    
    _currentProcess!.stderr
        .transform(const SystemEncoding().decoder)
        .transform(const LineSplitter())
        .listen((line) {
      _minerLogController.add('[${DateTime.now().toString().substring(11, 19)}] [ERROR] $line');
    });
  }

  /// 监控进程退出
  void _monitorProcessExit() {
    if (_currentProcess == null) return;
    
    _currentProcess!.exitCode.then((exitCode) {
      _logger.warning('挖矿进程已退出，退出码: $exitCode');
      // 清理状态
      _currentProcess = null;
      final wasMining = _currentTask != null;
      _currentTask = null;
      _taskMonitor?.cancel();
      _taskMonitor = null;
      
      // 发送进程退出事件
      if (wasMining) {
        _processExitController.add(null);
      }
    }).catchError((e) {
      _logger.severe('监控进程退出失败: $e');
    });
  }

  /// 获取挖矿日志流
  Stream<String> get minerLogStream => _minerLogController.stream;

  MiningTaskInfo? get currentTask => _currentTask;
  bool get isMining => _currentProcess != null;
  
  /// 清理资源
  void dispose() {
    _minerLogController.close();
    _processExitController.close();
  }
}

class MiningConfig {
  final String? lolMinerPath;
  final String? rigelPath;
  final String? bzMinerPath;
  final bool proxyEnabled;
  final String? serverUrl;
  final String? updateUrl;

  MiningConfig({
    this.lolMinerPath,
    this.rigelPath,
    this.bzMinerPath,
    this.proxyEnabled = false,
    this.serverUrl,
    this.updateUrl,
  });
}
