// ============================================================
// ApiClient — Genex HTTP 客户端
//
// 基于 Dio 的单例 HTTP 客户端，提供：
//   1. 统一的 baseUrl / 超时配置
//   2. Authorization 头自动注入（setToken() 全局设置）
//   3. 401 自动 Token 刷新 + 原始请求重试（透明无感）
//   4. 并发安全：多个 401 同时触发只执行一次刷新（Completer 锁）
//   5. Token 彻底过期时回调 AuthService 清除状态并导航到登录页
//
// 依赖：
//   SessionStorage — 读取 RefreshToken 用于静默刷新
//   AuthService    — 通过回调更新内存中的 AuthResult（反向依赖，避免循环 import）
//
// 刷新端点：POST /api/v1/auth/refresh { refreshToken }
//   响应:  { data: { accessToken, refreshToken, expiresIn } }
//
// 跳过重试的端点（防止无限循环）：
//   /auth/refresh, /auth/login, /auth/register, /auth/login-phone
//   以及携带 Options(extra: {'skipAuthRetry': true}) 的请求
// ============================================================

import 'dart:async';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import '../storage/session_storage.dart';

/// Genex API 客户端 — 单例
class ApiClient {
  static ApiClient? _instance;
  late final Dio _dio;

  // ── Token 刷新并发锁 ────────────────────────────────────────────────────
  // 当多个请求同时收到 401 时，只有第一个会真正发起刷新请求；
  // 其余请求等待 _refreshCompleter.future，共享同一刷新结果。
  Completer<bool>? _refreshCompleter;

  // ── 反向回调（由 AuthService.restoreSession() 注册） ────────────────────
  // 避免 api_client.dart 直接 import auth_service.dart（会造成循环引用）
  /// Token 刷新成功后，更新 AuthService 的内存状态
  Function(String accessToken, String refreshToken)? _onTokenRefreshed;

  /// Token 彻底过期（refresh 也失败）时，清除 AuthService 状态
  VoidCallback? _onSessionExpired;

  // ── 构造 ─────────────────────────────────────────────────────────────────

  ApiClient._({required String baseUrl}) {
    _dio = Dio(BaseOptions(
      baseUrl: baseUrl,
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 30),
      sendTimeout: const Duration(seconds: 30),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ));
    _dio.interceptors.add(_buildAuthInterceptor());
  }

  /// 默认 API 地址（走 Nginx 反向代理 → Kong 网关 :8080）
  static const String defaultBaseUrl = 'https://api.gogenex.com';

  static ApiClient get instance {
    _instance ??= ApiClient._(baseUrl: defaultBaseUrl);
    return _instance!;
  }

  static void initialize({required String baseUrl}) {
    _instance = ApiClient._(baseUrl: baseUrl);
  }

  Dio get dio => _dio;

  // ── 公开 API ──────────────────────────────────────────────────────────────

  /// 设置 / 清除 JWT Authorization 头
  ///
  /// 传入 null 表示登出，移除 Authorization 头。
  void setToken(String? token) {
    if (token != null) {
      _dio.options.headers['Authorization'] = 'Bearer $token';
    } else {
      _dio.options.headers.remove('Authorization');
    }
  }

  /// 注册 Auth 回调（AuthService.restoreSession() 中调用一次即可）
  ///
  /// [onTokenRefreshed]  — 刷新成功后更新内存中的 AuthResult
  /// [onSessionExpired]  — 刷新失败后清除 AuthService 状态并触发导航
  void configureAuthCallbacks({
    required Function(String accessToken, String refreshToken) onTokenRefreshed,
    required VoidCallback onSessionExpired,
  }) {
    _onTokenRefreshed = onTokenRefreshed;
    _onSessionExpired = onSessionExpired;
  }

  // ── HTTP 方法 ──────────────────────────────────────────────────────────────

  Future<Response> get(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return _dio.get(path, queryParameters: queryParameters, options: options);
  }

  Future<Response> post(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return _dio.post(path, data: data, queryParameters: queryParameters, options: options);
  }

  Future<Response> put(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return _dio.put(path, data: data, queryParameters: queryParameters, options: options);
  }

  Future<Response> delete(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) {
    return _dio.delete(path, data: data, queryParameters: queryParameters, options: options);
  }

  // ── 401 拦截器 ─────────────────────────────────────────────────────────────

  /// 构建认证拦截器
  ///
  /// 仅处理 401 错误：
  ///   1. 判断是否为可重试的端点（auth 相关端点不重试）
  ///   2. 尝试刷新 Token（带并发锁）
  ///   3. 刷新成功 → 用新 Token 重试原始请求
  ///   4. 刷新失败 → 通知 AuthService 清除会话，传递原始错误
  InterceptorsWrapper _buildAuthInterceptor() {
    return InterceptorsWrapper(
      onError: (DioException error, ErrorInterceptorHandler handler) async {
        // 非 401 错误直接透传
        if (error.response?.statusCode != 401) {
          return handler.next(error);
        }

        final path = error.requestOptions.path;

        // 这些端点收到 401 是正常业务逻辑（账号密码错误等），不尝试刷新
        final isAuthEndpoint = path.contains('/auth/refresh') ||
            path.contains('/auth/login') ||
            path.contains('/auth/register') ||
            path.contains('/auth/login-phone') ||
            path.contains('/auth/sms/send');

        // 手动标记跳过重试的请求（如刷新请求自身）
        final skipRetry = error.requestOptions.extra['skipAuthRetry'] == true;

        if (isAuthEndpoint || skipRetry) {
          return handler.next(error);
        }

        // ── 尝试静默刷新 Token ────────────────────────────────────────────
        final refreshed = await _tryRefreshToken();

        if (refreshed) {
          // 刷新成功：用新 Token 重试原始请求
          try {
            final retryResp = await _retryWithNewToken(error.requestOptions);
            return handler.resolve(retryResp);
          } catch (_) {
            // 重试也失败，透传原始错误（不清除会话，避免频繁跳登录页）
            return handler.next(error);
          }
        } else {
          // Access Token + Refresh Token 均已过期 → 强制登出
          _onSessionExpired?.call();
          return handler.next(error);
        }
      },
    );
  }

  // ── 私有：Token 刷新逻辑 ──────────────────────────────────────────────────

  /// 刷新 Token（带 Completer 并发锁）
  ///
  /// 若已有刷新在进行中，新的调用者直接等待已有刷新的结果，
  /// 不会重复发起刷新请求。
  ///
  /// 返回 true  — 刷新成功，新 Token 已写入 SessionStorage + Dio headers
  /// 返回 false — 刷新失败（RefreshToken 过期或网络错误）
  Future<bool> _tryRefreshToken() async {
    // 已有刷新进行中，等待其结果
    if (_refreshCompleter != null) {
      return _refreshCompleter!.future;
    }

    _refreshCompleter = Completer<bool>();
    try {
      // 从安全存储读取 RefreshToken
      final refreshToken = await SessionStorage.instance.getRefreshToken();
      if (refreshToken == null || refreshToken.isEmpty) {
        _refreshCompleter!.complete(false);
        return false;
      }

      // 发起刷新请求（extra skipAuthRetry: true 防止拦截器递归处理 401）
      final resp = await _dio.post(
        '/api/v1/auth/refresh',
        data: {'refreshToken': refreshToken},
        options: Options(extra: {'skipAuthRetry': true}),
      );

      final tokens = resp.data['data'] as Map<String, dynamic>;
      final newAccess = tokens['accessToken'] as String;
      final newRefresh = tokens['refreshToken'] as String;

      // 持久化新 Token
      await SessionStorage.instance.updateTokens(newAccess, newRefresh);

      // 更新 Dio 全局 Authorization 头，后续请求自动携带
      setToken(newAccess);

      // 通知 AuthService 同步内存中的 AuthResult
      _onTokenRefreshed?.call(newAccess, newRefresh);

      _refreshCompleter!.complete(true);
      return true;
    } catch (_) {
      _refreshCompleter!.complete(false);
      return false;
    } finally {
      // 无论成败都释放锁，允许下一轮刷新
      _refreshCompleter = null;
    }
  }

  /// 使用新 Token 重试原始请求
  ///
  /// 从当前 Dio 全局头读取最新的 Authorization，替换原始请求头后重发。
  Future<Response> _retryWithNewToken(RequestOptions original) {
    final headers = Map<String, dynamic>.from(original.headers);
    // 将刚刷新的 Token 注入重试请求头
    final authHeader = _dio.options.headers['Authorization'];
    if (authHeader != null) {
      headers['Authorization'] = authHeader;
    }
    return _dio.request(
      original.path,
      data: original.data,
      queryParameters: original.queryParameters,
      options: Options(
        method: original.method,
        headers: headers,
        extra: original.extra,
      ),
    );
  }
}
