// ============================================================
// AuthService — 认证服务
//
// 职责：
//   1. 封装所有 auth-service API 调用（注册/登录/刷新/登出……）
//   2. 维护内存中的认证状态（authState ValueNotifier）
//   3. 持久化 Token 到安全存储（via SessionStorage）
//   4. App 冷启动时恢复会话（restoreSession）
//   5. 向 ApiClient 注册 Token 刷新/过期回调（反向依赖）
//
// Token 生命周期：
//   登录/注册 → _setAuth() → 存 SecureStorage + 更新 ApiClient Header + 设 authState
//   401 触发  → ApiClient 拦截器自动刷新 → 回调 _onTokenRefreshed → 更新 authState
//   过期彻底  → ApiClient 回调 _clearAuth → 清 SecureStorage + 清 authState → 导航 /
//   主动登出  → logout() → DELETE /api/v1/auth/logout → _clearAuth()
//
// 依赖：
//   ApiClient      — HTTP 层（lib/core/network/api_client.dart）
//   SessionStorage — 安全持久化（lib/core/storage/session_storage.dart）
// ============================================================

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import '../network/api_client.dart';
import '../storage/session_storage.dart';
import '../telemetry/telemetry_service.dart';

/// SMS 验证码类型
enum SmsCodeType {
  register('REGISTER'),
  login('LOGIN'),
  resetPassword('RESET_PASSWORD'),
  changePhone('CHANGE_PHONE');

  final String value;
  const SmsCodeType(this.value);
}

/// 邮件验证码类型
enum EmailCodeType {
  register('REGISTER'),
  login('LOGIN'),
  resetPassword('RESET_PASSWORD'),
  changeEmail('CHANGE_EMAIL');

  final String value;
  const EmailCodeType(this.value);
}

/// 认证结果（登录/注册/Token 刷新后由后端返回）
///
/// 后端响应结构（/api/v1/auth/login 等）：
/// ```json
/// {
///   "code": 0,
///   "data": {
///     "user": { "id": "uuid", "phone": "+8618....", "nickname": "..." },
///     "tokens": {
///       "accessToken":  "eyJ...",
///       "refreshToken": "eyJ...",
///       "expiresIn":    900        // Access Token 有效秒数（如 900 = 15 min）
///     }
///   }
/// }
/// ```
class AuthResult {
  final Map<String, dynamic> user;
  final String accessToken;
  final String refreshToken;
  final int expiresIn;

  const AuthResult({
    required this.user,
    required this.accessToken,
    required this.refreshToken,
    required this.expiresIn,
  });

  factory AuthResult.fromJson(Map<String, dynamic> json) {
    final tokens = json['tokens'] as Map<String, dynamic>;
    return AuthResult(
      user: json['user'] as Map<String, dynamic>,
      accessToken: tokens['accessToken'] as String,
      refreshToken: tokens['refreshToken'] as String,
      expiresIn: tokens['expiresIn'] as int,
    );
  }
}

/// 认证服务 — 单例
class AuthService {
  static final AuthService _instance = AuthService._();
  static AuthService get instance => _instance;
  AuthService._();

  final _api = ApiClient.instance;

  // ── 状态 ─────────────────────────────────────────────────────────────────

  /// 当前登录用户的认证结果；null 表示未登录
  ///
  /// 监听此 Notifier 可感知登录/登出状态变化。
  /// main.dart 中用于：
  ///   - 初始路由判断（isLoggedIn → '/main' 还是 '/'）
  ///   - Session 过期后自动导航回欢迎页
  final ValueNotifier<AuthResult?> authState = ValueNotifier(null);

  /// 是否已登录
  bool get isLoggedIn => authState.value != null;

  // ── 启动恢复 ──────────────────────────────────────────────────────────────

  /// 从安全存储恢复会话（App 冷启动时调用，需在 runApp 之前 await）
  ///
  /// 执行顺序：
  ///   1. 向 ApiClient 注册 Token 刷新 / 过期回调（只需注册一次）
  ///   2. 从 SecureStorage 读取上次保存的 Token + 用户信息
  ///   3. 若存在：恢复 authState 并设置 ApiClient Authorization 头
  ///   4. 若不存在：返回 false，调用方将显示欢迎/登录页
  ///
  /// 不主动校验 Token 有效性——
  /// 若 Access Token 已过期，ApiClient 拦截器会在首次 API 调用时自动刷新；
  /// 若 Refresh Token 也过期，拦截器回调 [_clearAuth]，驱动导航回 '/'。
  ///
  /// Returns true — 已恢复会话（进入 /main）
  /// Returns false — 无会话记录（显示欢迎页）
  Future<bool> restoreSession() async {
    // 1. 注册回调（ApiClient 的反向依赖）
    _api.configureAuthCallbacks(
      // Token 静默刷新成功：更新内存中的 AuthResult
      onTokenRefreshed: (newAccess, newRefresh) {
        final current = authState.value;
        if (current != null) {
          authState.value = AuthResult(
            user: current.user,
            accessToken: newAccess,
            refreshToken: newRefresh,
            expiresIn: 0, // 刷新后不重置计时器，依赖拦截器持续保活
          );
        }
      },
      // 双 Token 均过期：清除状态（ValueNotifier 变化触发 main.dart 导航）
      onSessionExpired: _clearAuth,
    );

    // 2. 尝试从 SecureStorage 加载
    final saved = await SessionStorage.instance.load();
    if (saved == null) return false;

    // 3. 恢复内存状态（expiresIn=0 表示剩余时间未知，由拦截器按需刷新）
    authState.value = AuthResult(
      user: saved.user,
      accessToken: saved.accessToken,
      refreshToken: saved.refreshToken,
      expiresIn: 0,
    );
    _api.setToken(saved.accessToken);
    return true;
  }

  // ── SMS 验证码 ─────────────────────────────────────────────────────────────

  /// 发送短信验证码
  ///
  /// [type] 决定后端使用的模板：REGISTER / LOGIN / RESET_PASSWORD / CHANGE_PHONE
  ///
  /// Returns expiresIn (秒)，UI 用于倒计时展示
  Future<int> sendSmsCode(String phone, SmsCodeType type) async {
    final resp = await _api.post('/api/v1/auth/sms/send', data: {
      'phone': phone,
      'type': type.value,
    });
    final data = resp.data['data'] as Map<String, dynamic>;
    return data['expiresIn'] as int;
  }

  // ── 邮件验证码 ────────────────────────────────────────────────────────────

  /// 发送邮件验证码
  ///
  /// [type] 决定后端使用的模板：REGISTER / LOGIN / RESET_PASSWORD / CHANGE_EMAIL
  ///
  /// Returns expiresIn (秒)，UI 用于倒计时展示
  Future<int> sendEmailCode(String email, EmailCodeType type) async {
    final resp = await _api.post('/api/v1/auth/email/send', data: {
      'email': email,
      'type': type.value,
    });
    final data = resp.data['data'] as Map<String, dynamic>;
    return data['expiresIn'] as int;
  }

  // ── 邮箱注册 ─────────────────────────────────────────────────────────────

  /// 邮箱注册（需先用 EmailCodeType.register 获取验证码）
  ///
  /// 注册成功后自动登录（调用 _setAuth 保存 Token + 设置请求头）。
  Future<AuthResult> registerByEmail({
    required String email,
    required String emailCode,
    required String password,
    String? nickname,
    String? referralCode,
  }) async {
    final resp = await _api.post('/api/v1/auth/register-email', data: {
      'email': email,
      'emailCode': emailCode,
      'password': password,
      if (nickname != null) 'nickname': nickname,
      if (referralCode != null && referralCode.isNotEmpty)
        'referralCode': referralCode.toUpperCase(),
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── 邮件验证码登录 ────────────────────────────────────────────────────────

  /// 邮件验证码登录（需先用 EmailCodeType.login 获取验证码）
  Future<AuthResult> loginByEmail({
    required String email,
    required String emailCode,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/login-email', data: {
      'email': email,
      'emailCode': emailCode,
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── 微信登录 ──────────────────────────────────────────────────────────────

  /// 微信一键登录 / 自动注册
  ///
  /// [code] 来自 fluwx WXAuthResp.code（微信 SDK 授权后返回，一次性，5 分钟有效）
  /// [referralCode] 可选推荐码，仅新用户注册时生效
  Future<AuthResult> loginByWechat({
    required String code,
    String? referralCode,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/wechat', data: {
      'code': code,
      if (referralCode != null && referralCode.isNotEmpty)
        'referralCode': referralCode.toUpperCase(),
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── 支付宝登录 ────────────────────────────────────────────────────────────

  /// 获取支付宝移动端签名授权字符串（tobias 3.x+ 需要）
  ///
  /// tobias 3.x 后不再支持客户端直接传 appId+scope，
  /// 需要由后端用 RSA2 私钥生成签名后的 authString，
  /// 再传给 `Tobias().auth(authString)` 拉起支付宝授权页。
  ///
  /// 返回: `{ 'authString': '...' }`
  Future<String> getAlipayAuthString() async {
    final resp = await _api.get('/api/v1/auth/alipay/auth-string');
    return resp.data['data']['authString'] as String;
  }

  /// 支付宝一键登录 / 自动注册
  ///
  /// [authCode] 来自 tobias Tobias().auth() 回调中的 auth_code（一次性，3 分钟有效）
  Future<AuthResult> loginByAlipay({
    required String authCode,
    String? referralCode,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/alipay', data: {
      'authCode': authCode,
      if (referralCode != null && referralCode.isNotEmpty)
        'referralCode': referralCode.toUpperCase(),
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── Google 登录 ────────────────────────────────────────────────────────────

  /// Google 一键登录 / 自动注册
  ///
  /// [idToken] 来自 google_sign_in GoogleSignInAuthentication.idToken（JWT）
  Future<AuthResult> loginByGoogle({
    required String idToken,
    String? referralCode,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/google', data: {
      'idToken': idToken,
      if (referralCode != null && referralCode.isNotEmpty)
        'referralCode': referralCode.toUpperCase(),
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── Apple 登录 ────────────────────────────────────────────────────────────

  /// Apple 一键登录 / 自动注册（仅 iOS）
  ///
  /// [identityToken] 来自 sign_in_with_apple AppleIDCredential.identityToken
  /// [displayName]  用户授权时填写的显示名（Apple 首次授权时提供，之后为 null）
  Future<AuthResult> loginByApple({
    required String identityToken,
    String? displayName,
    String? referralCode,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/apple', data: {
      'identityToken': identityToken,
      if (displayName != null && displayName.isNotEmpty) 'displayName': displayName,
      if (referralCode != null && referralCode.isNotEmpty)
        'referralCode': referralCode.toUpperCase(),
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── 邮件重置密码 ──────────────────────────────────────────────────────────

  /// 通过邮件验证码重置密码（忘记密码场景，需先用 EmailCodeType.resetPassword 获取验证码）
  Future<void> resetPasswordByEmail({
    required String email,
    required String emailCode,
    required String newPassword,
  }) async {
    await _api.post('/api/v1/auth/reset-password-email', data: {
      'email': email,
      'emailCode': emailCode,
      'newPassword': newPassword,
    });
  }

  // ── 推荐码 ─────────────────────────────────────────────────────────────────

  /// 验证推荐码是否有效（注册页实时校验用，不需要登录）
  Future<bool> validateReferralCode(String code) async {
    try {
      final resp = await _api.get(
        '/api/v1/referral/validate',
        queryParameters: {'code': code},
      );
      final data = resp.data['data'] as Map<String, dynamic>;
      return data['valid'] == true;
    } catch (_) {
      return false; // 网络异常时降级为不阻断注册
    }
  }

  // ── 注册 ─────────────────────────────────────────────────────────────────

  /// 手机号注册（需先用 SmsCodeType.register 获取验证码）
  ///
  /// 注册成功后自动登录（调用 _setAuth 保存 Token + 设置请求头）。
  Future<AuthResult> register({
    required String phone,
    required String smsCode,
    required String password,
    String? nickname,
    String? referralCode,
  }) async {
    final resp = await _api.post('/api/v1/auth/register', data: {
      'phone': phone,
      'smsCode': smsCode,
      'password': password,
      if (nickname != null) 'nickname': nickname,
      if (referralCode != null && referralCode.isNotEmpty)
        'referralCode': referralCode.toUpperCase(),
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── 登录 ─────────────────────────────────────────────────────────────────

  /// 密码登录
  ///
  /// [identifier] 可为手机号（E.164 或裸号，后端会归一化）或邮箱
  Future<AuthResult> loginByPassword({
    required String identifier,
    required String password,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/login', data: {
      'identifier': identifier,
      'password': password,
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  /// 短信验证码登录（需先用 SmsCodeType.login 获取验证码）
  Future<AuthResult> loginByPhone({
    required String phone,
    required String smsCode,
    String? deviceInfo,
  }) async {
    final resp = await _api.post('/api/v1/auth/login-phone', data: {
      'phone': phone,
      'smsCode': smsCode,
      if (deviceInfo != null) 'deviceInfo': deviceInfo,
    });
    final result = AuthResult.fromJson(resp.data['data']);
    await _setAuth(result);
    return result;
  }

  // ── 密码管理 ──────────────────────────────────────────────────────────────

  /// 重置密码（忘记密码场景，需先用 SmsCodeType.resetPassword 获取验证码）
  Future<void> resetPassword({
    required String phone,
    required String smsCode,
    required String newPassword,
  }) async {
    await _api.post('/api/v1/auth/reset-password', data: {
      'phone': phone,
      'smsCode': smsCode,
      'newPassword': newPassword,
    });
  }

  /// 修改密码（已登录状态，需要当前密码验证）
  ///
  /// 修改成功后强制登出——旧 Token 会被后端撤销。
  Future<void> changePassword({
    required String oldPassword,
    required String newPassword,
  }) async {
    await _api.post('/api/v1/auth/change-password', data: {
      'oldPassword': oldPassword,
      'newPassword': newPassword,
    });
    await logout();
  }

  // ── 手机号管理 ────────────────────────────────────────────────────────────

  /// 换绑手机号（需登录 + SmsCodeType.changePhone 验证码）
  Future<void> changePhone({
    required String newPhone,
    required String newSmsCode,
  }) async {
    await _api.post('/api/v1/auth/change-phone', data: {
      'newPhone': newPhone,
      'newSmsCode': newSmsCode,
    });
  }

  // ── Token 管理 ────────────────────────────────────────────────────────────

  /// 主动刷新 Token（一般不需要手动调用，ApiClient 拦截器会自动触发）
  ///
  /// 使用场景：定时主动刷新（如 Access Token 快过期时提前刷新）。
  Future<void> refreshToken() async {
    final current = authState.value;
    if (current == null) return;

    final resp = await _api.post(
      '/api/v1/auth/refresh',
      data: {'refreshToken': current.refreshToken},
      options: Options(extra: {'skipAuthRetry': true}),
    );
    final tokens = resp.data['data'] as Map<String, dynamic>;
    final newResult = AuthResult(
      user: current.user,
      accessToken: tokens['accessToken'] as String,
      refreshToken: tokens['refreshToken'] as String,
      expiresIn: tokens['expiresIn'] as int,
    );
    await _setAuth(newResult);
  }

  /// 主动登出
  ///
  /// 通知后端撤销 Token（fire-and-forget，即使失败也清除本地状态）。
  Future<void> logout() async {
    try {
      await _api.post('/api/v1/auth/logout');
    } catch (_) {
      // 网络失败不影响本地登出
    }
    await _clearAuth();
  }

  // ── 私有 ──────────────────────────────────────────────────────────────────

  /// 登录/注册/Token 刷新成功后统一调用
  ///
  /// 1. 更新内存 authState（触发 UI 响应）
  /// 2. 更新 ApiClient Authorization 头
  /// 3. 持久化到 SecureStorage（异步，不影响返回）
  Future<void> _setAuth(AuthResult result) async {
    authState.value = result;
    _api.setToken(result.accessToken);
    await SessionStorage.instance.save(
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      user: result.user,
    );
    // 遥测：登录成功后注入 userId 和 token
    if (TelemetryService().isInitialized) {
      TelemetryService().setUserId(result.user['id'] as String?);
      TelemetryService().setAccessToken(result.accessToken);
    }
  }

  /// 登出/Token 过期时统一调用
  ///
  /// 1. 清除内存 authState（ValueNotifier 通知 main.dart 监听者导航到 '/'）
  /// 2. 清除 ApiClient Authorization 头
  /// 3. 清除 SecureStorage
  Future<void> _clearAuth() async {
    authState.value = null;
    _api.setToken(null);
    await SessionStorage.instance.clear();
    // 遥测：退出登录时清除 userId 和 token
    if (TelemetryService().isInitialized) {
      TelemetryService().clearUserId();
      TelemetryService().clearAccessToken();
    }
  }
}
