import 'package:dio/dio.dart';

import '../../domain/errors.dart';
import '../../contracts/error.dart';
import '../../domain/ports/server_sync_gateway.dart';
import '../../../shared/utils/app_logger.dart';
import '../../../shared/utils/url_utils.dart';


class ServerSyncClient implements ServerSyncGateway {
  static const _tag = 'ServerSync';

  final Dio _dio;

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

  @override
  Future<ServerSyncSnapshot> pull({
    required String baseUrl,
    required String accessToken,
  }) async {
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/server-sync';
    try {
      final res = await _dio.get<Map<String, dynamic>>(
        url,
        options: Options(headers: {'Authorization': 'Bearer $accessToken'}),
      );
      final status = res.statusCode ?? 0;
      if (!_isSuccess(status)) {
        throw _httpError(status, res.data);
      }
      final body = res.data ?? const <String, dynamic>{};
      return ServerSyncSnapshot(blob: body['blob'] as String?);
    } on DioException catch (e) {
      throw _networkError(e);
    }
  }

  @override
  Future<void> push({
    required String baseUrl,
    required String accessToken,
    required String blob,
  }) async {
    final url = '${stripTrailingSlash(baseUrl.trim())}/api/user/server-sync';
    try {
      final res = await _dio.put<Map<String, dynamic>>(
        url,
        data: {'blob': blob},
        options: Options(headers: {'Authorization': 'Bearer $accessToken'}),
      );
      final status = res.statusCode ?? 0;
      if (!_isSuccess(status)) {
        throw _httpError(status, res.data);
      }
    } on DioException catch (e) {
      throw _networkError(e);
    }
  }

  DomainError _httpError(int status, Map<String, dynamic>? body) {
    final message = (body?['message'] as String?)?.trim();
    AppLogger.warn(_tag, 'HTTP $status ${message ?? ''}');
    return switch (status) {
      401 || 403 => DomainError(
        ErrorCode.authForbidden,
        message ?? '账号登录已过期，请重新登录',
        details: {'status': status},
      ),
      413 => DomainError(
        ErrorCode.invalidParams,
        message ?? '同步数据过大（上限 256KB）',
        details: {'status': status},
      ),
      400 => DomainError(
        ErrorCode.invalidParams,
        message ?? '请求无效',
        details: {'status': status},
      ),
      _ => DomainError(
        ErrorCode.serverHttpError,
        message ?? '同步失败（HTTP $status）',
        retryable: true,
        details: {'status': status},
      ),
    };
  }

  DomainError _networkError(DioException e) {
    AppLogger.warn(_tag, 'network error', e);
    return switch (e.type) {
      DioExceptionType.connectionTimeout ||
      DioExceptionType.receiveTimeout ||
      DioExceptionType.sendTimeout => DomainError(
        ErrorCode.serverTimeout,
        '连接超时，请检查网络',
        retryable: true,
      ),
      _ => DomainError(
        ErrorCode.serverUnreachable,
        '无法连接服务器，请检查网络',
        retryable: true,
      ),
    };
  }

  static bool _isSuccess(int status) => status >= 200 && status < 300;
}
