import 'dart:io';
import 'package:dio/dio.dart';
import 'failures.dart';

class ErrorHandler {
  /// Converts any error into a [Failure] with a user-friendly Chinese message.
  static Failure handle(dynamic error) {
    if (error is Failure) return error;

    if (error is DioException) {
      return _handleDio(error);
    }

    if (error is SocketException) {
      return const NetworkFailure('无法连接到服务器，请检查网络');
    }

    if (error is FormatException) {
      return const ServerFailure('数据格式异常', statusCode: null);
    }

    return ServerFailure('发生未知错误: ${_truncate(error.toString(), 80)}');
  }

  /// Converts any error into a user-friendly Chinese message string.
  /// Use this everywhere instead of `e.toString()`.
  static String friendlyMessage(dynamic error) {
    return handle(error).message;
  }

  static Failure _handleDio(DioException error) {
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
        return const TimeoutFailure('连接超时，服务器无响应');

      case DioExceptionType.sendTimeout:
        return const TimeoutFailure('发送请求超时，请检查网络');

      case DioExceptionType.receiveTimeout:
        return const TimeoutFailure('等待响应超时，请稍后重试');

      case DioExceptionType.connectionError:
        return NetworkFailure(_connectionErrorMessage(error));

      case DioExceptionType.badCertificate:
        return const NetworkFailure('安全证书验证失败');

      case DioExceptionType.badResponse:
        return _handleBadResponse(error.response);

      case DioExceptionType.cancel:
        return const ServerFailure('请求已取消');

      case DioExceptionType.unknown:
        return NetworkFailure(_unknownErrorMessage(error));
    }
  }

  static Failure _handleBadResponse(Response? response) {
    final statusCode = response?.statusCode;
    // Try to extract server-side error message
    final serverMsg = _extractServerMessage(response);

    switch (statusCode) {
      case 400:
        return ServerFailure(serverMsg ?? '请求参数错误', statusCode: 400);
      case 401:
        return const AuthFailure();
      case 403:
        return const ServerFailure('没有权限执行此操作', statusCode: 403);
      case 404:
        return ServerFailure(serverMsg ?? '请求的资源不存在', statusCode: 404);
      case 409:
        return ServerFailure(serverMsg ?? '数据冲突，请刷新后重试', statusCode: 409);
      case 422:
        return ServerFailure(serverMsg ?? '提交的数据不合法', statusCode: 422);
      case 429:
        return const ServerFailure('请求过于频繁，请稍后重试', statusCode: 429);
      case 500:
        return const ServerFailure('服务器内部错误，请稍后重试', statusCode: 500);
      case 502:
        return const ServerFailure('服务器网关错误，请稍后重试', statusCode: 502);
      case 503:
        return const ServerFailure('服务器暂时不可用，请稍后重试', statusCode: 503);
      default:
        return ServerFailure(
          serverMsg ?? '服务器错误 ($statusCode)',
          statusCode: statusCode,
        );
    }
  }

  static String? _extractServerMessage(Response? response) {
    try {
      final data = response?.data;
      if (data is Map) {
        return data['message'] as String? ?? data['error'] as String?;
      }
    } catch (_) {}
    return null;
  }

  static String _connectionErrorMessage(DioException error) {
    final innerError = error.error;
    if (innerError is SocketException) {
      if (innerError.osError?.errorCode == 111 ||
          innerError.osError?.errorCode == 10061) {
        return '服务器拒绝连接，请确认服务是否启动';
      }
      return '无法连接到服务器，请检查网络';
    }
    return '网络连接失败，请检查网络设置';
  }

  static String _unknownErrorMessage(DioException error) {
    final errorStr = error.error?.toString() ?? '';
    if (errorStr.contains('Connection reset')) {
      return '连接被服务器重置，请稍后重试';
    }
    if (errorStr.contains('Connection refused')) {
      return '服务器拒绝连接，请确认服务是否启动';
    }
    if (errorStr.contains('Connection closed')) {
      return '连接已关闭，请稍后重试';
    }
    if (errorStr.contains('SocketException')) {
      return '网络连接异常，请检查网络设置';
    }
    if (errorStr.contains('HandshakeException') ||
        errorStr.contains('TlsException')) {
      return '安全连接失败，请检查网络环境';
    }
    return '网络请求失败，请检查网络后重试';
  }

  static String _truncate(String s, int maxLen) {
    return s.length > maxLen ? '${s.substring(0, maxLen)}...' : s;
  }
}
