import 'package:dio/dio.dart';

import '../../../shared/utils/app_logger.dart';
import '../../../shared/utils/url_utils.dart';
import '../../domain/ports/sm_account_gateway.dart';


class SmAccountClient implements SmAccountGateway {
  static const _tag = 'SmAccount';

  final Dio _dio;

  SmAccountClient({Dio? dio})
    : _dio =
          dio ??
          Dio(
            BaseOptions(
              connectTimeout: const Duration(seconds: 8),
              receiveTimeout: const Duration(seconds: 12),
              headers: {'Accept': 'application/json'},
              validateStatus: (status) => status != null && status < 500,
            ),
          );


  Future<T> _guard<T>(
    String op,
    Future<T> Function() body, {
    required T Function(String message) failure,
    required String fallbackMessage,
  }) async {
    try {
      return await body();
    } on DioException catch (e) {
      AppLogger.warn(_tag, '$op network error', e);
      return failure(_networkHint(e));
    } catch (e) {
      AppLogger.warn(_tag, '$op unexpected error', e);
      return failure(fallbackMessage);
    }
  }

  @override
  Future<SmLoginResult> login({
    required String baseUrl,
    required String email,
    required String password,
  }) => _guard('login', () async {
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/login';
    final res = await _dio.post<Map<String, dynamic>>(
      url,
      data: {'email': email, 'password': password},
    );
    return _sessionResultFrom(res, fallbackEmail: email);
  }, failure: SmLoginResult.failure, fallbackMessage: '登录失败，请稍后重试');

  @override
  Future<SmActionResult> registerStart({
    required String baseUrl,
    required String email,
  }) => _guard('registerStart', () async {
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/register/start';
    final res = await _dio.post<Map<String, dynamic>>(
      url,
      data: {'email': email},
    );
    return _actionResultFrom(res, 'registerStart');
  }, failure: SmActionResult.failure, fallbackMessage: '发送验证码失败，请稍后重试');

  @override
  Future<SmLoginResult> registerVerify({
    required String baseUrl,
    required String email,
    required String password,
    required String code,
  }) => _guard('registerVerify', () async {
    final url =
        '${stripTrailingSlash(baseUrl.trim())}/api/user/register/verify';
    final res = await _dio.post<Map<String, dynamic>>(
      url,
      data: {'email': email, 'password': password, 'code': code},
    );
    return _sessionResultFrom(res, fallbackEmail: email);
  }, failure: SmLoginResult.failure, fallbackMessage: '验证失败，请稍后重试');

  @override
  Future<SmActionResult> resendCode({
    required String baseUrl,
    required String email,
  }) => _guard('resendCode', () async {
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/resend-code';
    final res = await _dio.post<Map<String, dynamic>>(
      url,
      data: {'email': email, 'purpose': 'register'},
    );
    return _actionResultFrom(res, 'resendCode');
  }, failure: SmActionResult.failure, fallbackMessage: '重发验证码失败，请稍后重试');

  @override
  Future<SmTokenRefreshResult> refreshAccessToken({
    required String baseUrl,
    required String refreshToken,
  }) => _guard('refreshAccessToken', () async {
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/token/refresh';
    final res = await _dio.post<Map<String, dynamic>>(
      url,
      data: {'refreshToken': refreshToken},
    );
    final status = res.statusCode ?? 0;
    final body = res.data ?? const <String, dynamic>{};
    if (_isSuccess(status) && body['ok'] == true) {
      return SmTokenRefreshResult(
        ok: true,
        accessToken: (body['accessToken'] as String?) ?? '',
        refreshToken: (body['refreshToken'] as String?) ?? refreshToken,
        expiresIn: (body['expiresIn'] as num?)?.toInt() ?? 0,
        isVip: body['isVip'] == true,
        vipExpiresAt: (body['vipExpiresAt'] as num?)?.toInt(),
        vipPermanent: body['vipPermanent'] == true,
        hasVipInfo: body.containsKey('isVip'),
      );
    }
    final message = (body['message'] as String?)?.trim();
    AppLogger.warn(_tag, 'refreshAccessToken ← HTTP $status ${message ?? ''}');
    return SmTokenRefreshResult.failure(
      message ?? _statusHint(status),
      invalidRefreshToken: status == 401 || status == 403,
    );
  }, failure: SmTokenRefreshResult.failure, fallbackMessage: 'Token 刷新失败，请稍后重试');


  SmActionResult _actionResultFrom(
    Response<Map<String, dynamic>> res,
    String op,
  ) {
    final body = res.data ?? const <String, dynamic>{};
    if (_isSuccess(res.statusCode) && body['ok'] == true) {
      return SmActionResult.success();
    }
    final message = (body['message'] as String?)?.trim();
    AppLogger.warn(_tag, '$op ← HTTP ${res.statusCode} ${message ?? ''}');
    return SmActionResult.failure(message ?? _statusHint(res.statusCode ?? 0));
  }


  SmLoginResult _sessionResultFrom(
    Response<Map<String, dynamic>> res, {
    required String fallbackEmail,
  }) {
    final status = res.statusCode ?? 0;
    final body = res.data ?? const <String, dynamic>{};
    final ok = body['ok'] == true;

    if (_isSuccess(status) && ok) {
      final refreshToken = (body['refreshToken'] as String?)?.trim() ?? '';
      if (refreshToken.isEmpty) {
        AppLogger.warn(_tag, 'auth ok but missing refresh token');
        return SmLoginResult.failure('认证成功但未收到登录凭据，请稍后重试');
      }
      return SmLoginResult(
        ok: true,
        email: (body['email'] as String?)?.trim() ?? fallbackEmail.trim(),
        accessToken: (body['accessToken'] as String?) ?? '',
        refreshToken: refreshToken,
        expiresIn: (body['expiresIn'] as num?)?.toInt() ?? 0,
        isVip: body['isVip'] == true,
        vipExpiresAt: (body['vipExpiresAt'] as num?)?.toInt(),
        vipPermanent: body['vipPermanent'] == true,
      );
    }

    final message = (body['message'] as String?)?.trim();
    final needsVerification = body['needsVerification'] == true;
    AppLogger.warn(_tag, 'auth ← HTTP $status ${message ?? ''}');
    return SmLoginResult.failure(
      message ?? _statusHint(status),
      needsVerification: needsVerification,
    );
  }

  @override
  Future<void> logout({
    required String baseUrl,
    required String refreshToken,
  }) async {
    final token = refreshToken.trim();
    if (token.isEmpty) return;
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/token/logout';
    try {
      await _dio.post<Map<String, dynamic>>(url, data: {'refreshToken': token});
    } catch (e) {
      AppLogger.warn(_tag, 'logout failed (non-fatal)', e);
    }
  }


  static bool _isSuccess(int? status) =>
      status != null && status >= 200 && status < 300;

  static String _statusHint(int status) => switch (status) {
    401 => '邮箱或密码错误',
    403 => '邮箱尚未验证',
    429 => '请求过于频繁，请稍后再试',
    _ => '请求失败（HTTP $status）',
  };

  static String _networkHint(DioException e) => switch (e.type) {
    DioExceptionType.connectionTimeout ||
    DioExceptionType.receiveTimeout ||
    DioExceptionType.sendTimeout => '连接超时，请检查网络或服务器地址',
    DioExceptionType.connectionError => '无法连接服务器，请检查服务器地址',
    _ => '网络异常，请稍后重试',
  };
}
