// desktop_vpn_bridge.dart — 桌面端 VpnBridge 实现（tsk_SLCsjNgtmng3）
//
// 实现 VpnBridge 接口，底层驱动 KernelProcess（默认使用 DesktopKernelProcess）。
//
// 关键职责：
//   1. start(configJson):
//        - 注入 experimental.clash_api（随机高位端口 + 随机 secret）
//        - 写 config 到 <AppSupport>/pangolin/kernel/config_<ts>.json（权限 0600）
//        - 调 kernel.spawn(configPath)
//   2. stop(): kernel.kill()
//   3. statusStream / statsStream: 代理 KernelProcess 事件流
//   4. selectOutbound: 通过 Clash API PUT /proxies/{group} 切换出口
//
// macOS PoC 依赖:
//   · sing-box 需能建立 TUN 接口（sudo 或 sudoers 免密白名单）
//   · 环境变量 PANGOLIN_SINGBOX_BIN 可指定二进制路径
//   · 实际 REALITY 参数由调用方（PoC 脚本）以静态 config 传入
//
// ignore_for_file: avoid_print

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

import 'kernel_process.dart';
import 'vpn_bridge.dart';

/// 桌面端 VPN 桥接实现（macOS / Linux / Windows PoC）。
///
/// 使用示例（PoC 命令行测试）：
/// ```dart
/// final bridge = DesktopVpnBridge();
/// bridge.statusStream.listen(print);
/// bridge.statsStream.listen(print);
/// await bridge.start(jsonEncode(myConfig));
/// // ...
/// await bridge.stop();
/// bridge.dispose();
/// ```
class DesktopVpnBridge implements VpnBridge {
  /// 构造函数。
  ///
  /// [kernel] 可注入假实现，用于测试。默认创建 [DesktopKernelProcess]。
  /// [configDirOverride] 仅供测试使用，覆盖配置写入目录（否则写到应用支持目录）。
  DesktopVpnBridge({
    KernelProcess? kernel,
    bool useSudo = true,
    String? configDirOverride,
  })  : _kernel = kernel ??
            DesktopKernelProcess(
              readyTimeout: const Duration(seconds: 20),
              statsPollInterval: const Duration(seconds: 1),
              useSudo: useSudo,
            ),
        _configDirOverride = configDirOverride;

  final KernelProcess _kernel;
  final String? _configDirOverride;

  // ── VpnBridge: start ─────────────────────────────────────────

  @override
  Future<void> start(String configJson) async {
    // 1. 注入 Clash API 配置（随机端口 + secret）
    final (enrichedJson, port, secret) = injectClashApi(configJson);

    // 2. 写 config 到应用支持目录（0600 权限）
    final configPath = await writeConfig(enrichedJson);
    print('[DesktopVpnBridge] config written: $configPath');
    print('[DesktopVpnBridge] clash_api port=$port secret_len=${secret.length}');

    // 3. 启动内核子进程（blocking until Clash API ready or error）
    await _kernel.spawn(configPath);
  }

  // ── VpnBridge: stop ──────────────────────────────────────────

  @override
  Future<void> stop() => _kernel.kill(gracePeriod: const Duration(seconds: 5));

  // ── VpnBridge: getStatus ─────────────────────────────────────

  @override
  Future<VpnStatus> getStatus() async {
    return _kernel.isRunning ? VpnStatus.on : VpnStatus.off;
  }

  // ── VpnBridge: selectOutbound ─────────────────────────────────

  @override
  Future<void> selectOutbound(String tag) async {
    if (!_kernel.isRunning) {
      throw StateError('kernel not running; cannot selectOutbound');
    }
    // sing-box Selector 出口组名默认为 "proxy"；调用方可在 config 中自定义组名。
    try {
      await _kernel.clashApiClient.selectProxy('proxy', tag);
    } catch (e) {
      print('[DesktopVpnBridge] selectOutbound(tag=$tag) error: $e');
      rethrow;
    }
  }

  // ── VpnBridge: getActiveOutbound ─────────────────────────────

  @override
  Future<String> getActiveOutbound() async {
    if (!_kernel.isRunning) return 'auto';
    try {
      final data = await _kernel.clashApiClient.getProxies();
      final proxies = data['proxies'];
      if (proxies is Map) {
        final proxy = proxies['proxy'];
        if (proxy is Map) {
          return (proxy['now'] as String?) ?? 'auto';
        }
      }
    } catch (_) {}
    return 'auto';
  }

  // ── VpnBridge: setKillSwitch ─────────────────────────────────

  @override
  Future<void> setKillSwitch({required bool on}) async {
    // macOS PoC: TUN 的 strict_route=true 提供基础的 kill-switch 语义。
    // 细粒度 kill-switch 归 11G。
    print('[DesktopVpnBridge] setKillSwitch=$on (strict_route in TUN config)');
  }

  // ── VpnBridge: 事件流 ────────────────────────────────────────

  @override
  Stream<VpnStatus> get statusStream => _kernel.statusStream;

  @override
  Stream<VpnStatsEvent> get statsStream => _kernel.statsStream;

  // ── VpnBridge: dispose ───────────────────────────────────────

  @override
  void dispose() {
    if (_kernel is DesktopKernelProcess) {
      (_kernel as DesktopKernelProcess).dispose();
    }
  }

  // ── 内部: Clash API 注入（@visibleForTesting）─────────────────

  /// 检查 configJson 中是否有 experimental.clash_api；若无则注入随机端口+secret。
  /// 返回 (修改后 JSON, 端口, secret)。
  // @visibleForTesting
  static (String, int, String) injectClashApi(String configJson) {
    late Map<String, dynamic> cfg;
    try {
      cfg = jsonDecode(configJson) as Map<String, dynamic>;
    } catch (e) {
      throw FormatException('invalid configJson: $e');
    }

    // 若已有 clash_api，尊重现有值
    final rawExp = cfg['experimental'];
    final experimental = (rawExp is Map)
        ? rawExp.cast<String, dynamic>()
        : <String, dynamic>{};

    if (experimental.containsKey('clash_api')) {
      final api =
          (experimental['clash_api'] as Map).cast<String, dynamic>();
      final ctrl =
          (api['external_controller'] as String?) ?? '127.0.0.1:9090';
      final secret = (api['secret'] as String?) ?? '';
      final port = int.tryParse(ctrl.split(':').last) ?? 9090;
      return (configJson, port, secret);
    }

    // 注入随机 port + secret
    final port = generateClashApiPort();
    final secret = generateClashApiSecret();
    final updated = {
      ...cfg,
      'experimental': {
        ...experimental,
        'clash_api': {
          'external_controller': '127.0.0.1:$port',
          'secret': secret,
        },
      },
    };
    return (jsonEncode(updated), port, secret);
  }

  // ── 内部: 配置文件写入 ────────────────────────────────────────

  // @visibleForTesting
  Future<String> writeConfig(String configJson) async {
    final dir = await _resolveConfigDir();
    await dir.create(recursive: true);

    final ts = DateTime.now().millisecondsSinceEpoch;
    final file = File('${dir.path}/config_$ts.json');
    await file.writeAsString(configJson, flush: true);

    // 0600 权限（仅当前用户可读写）
    if (!Platform.isWindows) {
      try {
        await Process.run('chmod', ['600', file.path]);
      } catch (_) {
        print('[DesktopVpnBridge] warning: chmod 600 failed for ${file.path}');
      }
    }
    return file.path;
  }

  Future<Directory> _resolveConfigDir() async {
    if (_configDirOverride != null) {
      return Directory(_configDirOverride!);
    }
    return _defaultConfigDir();
  }

  static Future<Directory> _defaultConfigDir() async {
    if (Platform.isMacOS) {
      final home = Platform.environment['HOME'] ?? '/tmp';
      return Directory(
          '$home/Library/Application Support/com.pangolin.vpn/kernel');
    }
    if (Platform.isLinux) {
      final base = Platform.environment['XDG_DATA_HOME'] ??
          '${Platform.environment['HOME'] ?? '/tmp'}/.local/share';
      return Directory('$base/pangolin-vpn/kernel');
    }
    if (Platform.isWindows) {
      final appData =
          Platform.environment['LOCALAPPDATA'] ?? r'C:\ProgramData';
      return Directory('$appData\\Pangolin\\kernel');
    }
    return Directory('/tmp/pangolin/kernel');
  }
}
