// connect_api.dart — 控制面 connect 接口客户端
//
// 职责：POST /v1/nodes/:id/connect → 获取完整 sing-box config JSON。
// 关键约束（§3.1）：返回的 JSON 字符串必须原样透传给 VpnBridge.start，
// 调用方禁止对字符串做任何增删改；本类同样不修改响应体。
//
// M6 联调：mock 服务器地址由 --dart-define=PANGOLIN_API_URL 注入（默认 :8081）。
// tsk_nuoKSM4Vt-zK
import 'dart:convert';
import 'package:http/http.dart' as http;

/// 控制面接口调用失败时抛出。
/// [statusCode] < 0 表示网络层错误，-2 表示响应体 JSON 解析失败。
class ConnectApiException implements Exception {
  const ConnectApiException({
    required this.statusCode,
    required this.messageZh,
    required this.messageEn,
  });

  final int statusCode;
  final String messageZh;
  final String messageEn;

  @override
  String toString() => 'ConnectApiException($statusCode): $messageZh';
}

/// [ConnectApi] 封装 POST /v1/nodes/:id/connect 调用。
///
/// 可通过 [client] 注入自定义 [http.Client]（方便单测使用 MockClient）。
class ConnectApi {
  ConnectApi({
    required this.baseUrl,
    required this.authToken,
    http.Client? client,
  }) : _client = client ?? http.Client();

  final String baseUrl;
  final String authToken;
  final http.Client _client;

  /// 向控制面请求 [nodeId] 节点的完整 sing-box config JSON。
  ///
  /// 返回**原始响应体字符串**，调用方必须不做任何修改直接传入 `VpnBridge.start`。
  /// 异常：[ConnectApiException]（HTTP 非 200、超时、非法 JSON）。
  Future<String> fetchConfig({
    required String nodeId,
    required String deviceId,
    bool splitCN = false,
  }) async {
    // splitCN=true 时带 ?split_cn=1：控制面渲染国内 IP/域名直连(不走隧道，#5）。
    var uri = Uri.parse('$baseUrl/v1/nodes/$nodeId/connect');
    if (splitCN) {
      uri = uri.replace(queryParameters: {'split_cn': '1'});
    }
    final http.Response response;

    try {
      response = await _client
          .post(
            uri,
            headers: {
              'Content-Type': 'application/json',
              'Authorization': 'Bearer $authToken',
            },
            body: jsonEncode({'device_id': deviceId}),
          )
          .timeout(const Duration(seconds: 15));
    } on Exception catch (e) {
      throw ConnectApiException(
        statusCode: -1,
        messageZh: '网络请求失败，请检查连接后重试',
        messageEn: 'Network error: $e',
      );
    }

    if (response.statusCode != 200) {
      throw ConnectApiException(
        statusCode: response.statusCode,
        messageZh: '节点连接失败 (HTTP ${response.statusCode})',
        messageEn: 'Connect failed (HTTP ${response.statusCode})',
      );
    }

    // 仅做合法性校验，不修改响应体。
    try {
      jsonDecode(response.body);
    } catch (_) {
      throw const ConnectApiException(
        statusCode: -2,
        messageZh: '收到的配置格式无效，请稍后重试',
        messageEn: 'Invalid config JSON received from server',
      );
    }

    // 原样返回——调用方透传给 VpnBridge.start，不得在途中修改。
    return response.body;
  }

  void dispose() => _client.close();
}
