import 'dart:convert';

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;

/// 服务端 HTTP 基址（与信令同源）。部署时通过 --dart-define=API_BASE= 注入。
const String kApiBase = String.fromEnvironment(
  'API_BASE',
  defaultValue: 'https://www.ttstd.com',
);

/// 账号体系与自助接口封装（对应服务端 /api/auth/* 与 /api/client/*）。
///
/// 令牌存储策略：
///  - accessToken：内存保存（掉线即失，需重新登录）；
///  - refreshToken：flutter_secure_storage（Keychain / EncryptedSharedPreferences，一次性，ses_ 前缀）。
class ApiClient {
  ApiClient({http.Client? client}) : _http = client ?? http.Client();

  final http.Client _http;
  static const _storage = FlutterSecureStorage();

  static const _kAccess = 'ttstd.accessToken';
  static const _kRefresh = 'ttstd.refreshToken';
  static const _kUsername = 'ttstd.username';

  // 刷新单飞：避免并发触发多次刷新。
  Future<Map<String, dynamic>>? _refreshing;

  String? _accessToken;
  String? get accessToken => _accessToken;

  /// 从安全存储恢复令牌（应用启动时调用）。
  Future<void> restore() async {
    _accessToken = await _storage.read(key: _kAccess);
  }

  bool get hasTokens {
    // 同时读取内存 accessToken 与持久 refreshToken 判断。
    return _accessToken != null;
  }

  Future<bool> hasRefreshToken() async =>
      (await _storage.read(key: _kRefresh)) != null;

  Future<void> saveTokens({
    required String accessToken,
    String? refreshToken,
    String? username,
  }) async {
    _accessToken = accessToken;
    await _storage.write(key: _kAccess, value: accessToken);
    if (refreshToken != null) {
      await _storage.write(key: _kRefresh, value: refreshToken);
    }
    if (username != null) {
      await _storage.write(key: _kUsername, value: username);
    }
  }

  Future<String?> getUsername() => _storage.read(key: _kUsername);

  Future<void> clear() async {
    _accessToken = null;
    await _storage.delete(key: _kAccess);
    await _storage.delete(key: _kRefresh);
    await _storage.delete(key: _kUsername);
  }

  Future<Map<String, dynamic>> login(String username, String password) async {
    final data = await _post('/api/auth/login',
        body: {'username': username, 'password': password});
    await saveTokens(
      accessToken: data['accessToken'] as String,
      refreshToken: data['refreshToken'] as String?,
      username: username,
    );
    return data;
  }

  Future<Map<String, dynamic>> register(String username, String password) =>
      _post('/api/auth/register',
          body: {'username': username, 'password': password});

  /// 刷新令牌：带单飞锁，并发调用共享同一次刷新结果。可能轮换 refreshToken。
  Future<Map<String, dynamic>> refresh() async {
    final rt = await _storage.read(key: _kRefresh);
    if (rt == null) {
      await clear();
      throw ApiException('NO_REFRESH_TOKEN', 401);
    }
    _refreshing ??= _doRefresh(rt).whenComplete(() => _refreshing = null);
    return _refreshing!;
  }

  Future<Map<String, dynamic>> _doRefresh(String rt) async {
    final data = await _post('/api/auth/refresh', body: {'refreshToken': rt});
    await saveTokens(
      accessToken: data['accessToken'] as String,
      refreshToken: data['refreshToken'] as String?,
    );
    return data;
  }

  Future<Map<String, dynamic>> verify() => _get('/api/client/verify');

  Future<Map<String, dynamic>> bindings() => _get('/api/client/bindings');

  /// 拉取 TURN 短期凭证（服务端开启时返回 iceServers）；关闭时返回空 Map。
  Future<Map<String, dynamic>?> turnCredentials() async {
    try {
      return await _get('/api/client/turn-credentials');
    } on ApiException {
      return null;
    }
  }

  Future<Map<String, dynamic>> _post(String path, {required Map<String, dynamic> body}) async {
    final res = await _http.post(
      Uri.parse('$kApiBase$path'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode(body),
    );
    return _handle(res);
  }

  Future<Map<String, dynamic>> _get(String path) async {
    final res = await _http.get(
      Uri.parse('$kApiBase$path'),
      headers: {
        'Content-Type': 'application/json',
        if (_accessToken != null) 'Authorization': 'Bearer $_accessToken',
      },
    );
    return _handle(res);
  }

  Map<String, dynamic> _handle(http.Response res) {
    final body = res.body.isNotEmpty ? jsonDecode(res.body) : <String, dynamic>{};
    if (res.statusCode == 401) {
      throw ApiException(body['code'] ?? 'UNAUTHORIZED', 401);
    }
    if (res.statusCode < 200 || res.statusCode >= 300) {
      throw ApiException(body['error'] ?? 'HTTP ${res.statusCode}', res.statusCode);
    }
    return body as Map<String, dynamic>;
  }
}

/// API 调用异常，携带服务端 code 与 HTTP 状态码。
class ApiException implements Exception {
  final String code;
  final int httpCode;
  ApiException(this.code, this.httpCode);

  @override
  String toString() => code;
}
