import 'dart:developer' as developer;
import 'package:dio/dio.dart';
import '../constants/app_constants.dart';
import '../errors/exceptions.dart';
import '../storage/secure_storage.dart';

/// API client for making HTTP requests using Dio
/// Includes token management, request/response logging, and error handling
class ApiClient {
  late final Dio _dio;
  final SecureStorage _secureStorage;

  // Callback for 401 unauthorized errors (e.g., to navigate to login)
  void Function()? onUnauthorized;

  ApiClient(this._secureStorage, {this.onUnauthorized}) {
    _dio = Dio(
      BaseOptions(
        baseUrl: AppConstants.apiBaseUrl,
        connectTimeout: const Duration(milliseconds: AppConstants.connectionTimeout),
        receiveTimeout: const Duration(milliseconds: AppConstants.receiveTimeout),
        sendTimeout: const Duration(milliseconds: AppConstants.sendTimeout),
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
      ),
    );

    _setupInterceptors();
  }

  /// Setup all Dio interceptors
  void _setupInterceptors() {
    // Request interceptor - adds auth token and logs requests
    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (options, handler) async {
          // Add AccessToken header if available
          final token = await _secureStorage.getAccessToken();
          if (token != null && token.isNotEmpty) {
            options.headers['AccessToken'] = token;
          }

          // Add AppID header
          options.headers['AppID'] = AppConstants.appId;

          // Log request in debug mode
          _logRequest(options);

          return handler.next(options);
        },
        onResponse: (response, handler) {
          // Log response in debug mode
          _logResponse(response);

          return handler.next(response);
        },
        onError: (error, handler) async {
          // Log error in debug mode
          _logError(error);

          // Handle 401 unauthorized errors
          if (error.response?.statusCode == 401) {
            await _handle401Error();
          }

          return handler.next(error);
        },
      ),
    );
  }

  /// Make a GET request
  Future<Response<T>> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      return await _dio.get<T>(
        path,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
      );
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  /// Make a POST request
  Future<Response<T>> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      return await _dio.post<T>(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
      );
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  /// Make a PUT request
  Future<Response<T>> put<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      return await _dio.put<T>(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
      );
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  /// Make a DELETE request
  Future<Response<T>> delete<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    try {
      return await _dio.delete<T>(
        path,
        data: data,
        queryParameters: queryParameters,
        options: options,
        cancelToken: cancelToken,
      );
    } on DioException catch (e) {
      throw _handleDioError(e);
    }
  }

  /// Handle 401 Unauthorized errors
  Future<void> _handle401Error() async {
    developer.log(
      '401 Unauthorized - Clearing tokens and triggering logout',
      name: 'ApiClient',
      level: 900,
    );

    // Clear all tokens from secure storage
    await _secureStorage.clearTokens();

    // Trigger the unauthorized callback (e.g., navigate to login)
    if (onUnauthorized != null) {
      onUnauthorized!();
    }
  }

  /// Handle Dio errors and convert them to custom exceptions
  Exception _handleDioError(DioException error) {
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        return const NetworkException('Connection timeout. Please check your internet connection.');

      case DioExceptionType.badResponse:
        final statusCode = error.response?.statusCode;

        // Try to extract error message from API response
        String message = 'Server error occurred';

        if (error.response?.data is Map) {
          final data = error.response?.data as Map<String, dynamic>;

          // Check for standard API error format
          if (data['Errors'] != null && data['Errors'] is List && (data['Errors'] as List).isNotEmpty) {
            message = (data['Errors'] as List).first.toString();
          } else if (data['message'] != null) {
            message = data['message'].toString();
          } else if (data['error'] != null) {
            message = data['error'].toString();
          }
        }

        if (statusCode != null) {
          // Handle specific status codes
          switch (statusCode) {
            case 401:
              return const ServerException('Unauthorized. Please login again.', code: '401');
            case 403:
              return const ServerException('Forbidden. You do not have permission.', code: '403');
            case 404:
              return ServerException('Resource not found: $message', code: '404');
            case 422:
              return ServerException('Validation error: $message', code: '422');
            case 429:
              return const ServerException('Too many requests. Please try again later.', code: '429');
            case 500:
            case 501:
            case 502:
            case 503:
            case 504:
              return ServerException('Server error: $message (Status: $statusCode)', code: statusCode.toString());
            default:
              if (statusCode >= 400 && statusCode < 500) {
                return ServerException('Client error: $message (Status: $statusCode)', code: statusCode.toString());
              }
              return ServerException('HTTP error: $message (Status: $statusCode)', code: statusCode.toString());
          }
        }
        return ServerException('HTTP error: $message');

      case DioExceptionType.cancel:
        return const NetworkException('Request was cancelled');

      case DioExceptionType.connectionError:
        return const NetworkException('No internet connection. Please check your network settings.');

      case DioExceptionType.badCertificate:
        return const NetworkException('Certificate verification failed');

      case DioExceptionType.unknown:
        return ServerException('An unexpected error occurred: ${error.message}');
    }
  }

  /// Log request details
  void _logRequest(RequestOptions options) {
    developer.log(
      'REQUEST[${options.method}] => ${options.uri}',
      name: 'ApiClient',
      level: 800,
    );

    if (options.headers.isNotEmpty) {
      developer.log(
        'Headers: ${_sanitizeHeaders(options.headers)}',
        name: 'ApiClient',
        level: 800,
      );
    }

    if (options.queryParameters.isNotEmpty) {
      developer.log(
        'Query Params: ${options.queryParameters}',
        name: 'ApiClient',
        level: 800,
      );
    }

    if (options.data != null) {
      developer.log(
        'Body: ${options.data}',
        name: 'ApiClient',
        level: 800,
      );
    }
  }

  /// Log response details
  void _logResponse(Response response) {
    developer.log(
      'RESPONSE[${response.statusCode}] => ${response.requestOptions.uri}',
      name: 'ApiClient',
      level: 800,
    );

    developer.log(
      'Data: ${response.data}',
      name: 'ApiClient',
      level: 800,
    );
  }

  /// Log error details
  void _logError(DioException error) {
    developer.log(
      'ERROR[${error.response?.statusCode}] => ${error.requestOptions.uri}',
      name: 'ApiClient',
      level: 1000,
      error: error,
    );

    if (error.response?.data != null) {
      developer.log(
        'Error Data: ${error.response?.data}',
        name: 'ApiClient',
        level: 1000,
      );
    }
  }

  /// Sanitize headers to hide sensitive data in logs
  Map<String, dynamic> _sanitizeHeaders(Map<String, dynamic> headers) {
    final sanitized = Map<String, dynamic>.from(headers);

    // Hide Authorization token
    if (sanitized.containsKey('Authorization')) {
      sanitized['Authorization'] = '***REDACTED***';
    }

    // Hide any other sensitive headers
    final sensitiveKeys = ['api-key', 'x-api-key', 'token'];
    for (final key in sensitiveKeys) {
      if (sanitized.containsKey(key)) {
        sanitized[key] = '***REDACTED***';
      }
    }

    return sanitized;
  }

  /// Get the Dio instance (use carefully, prefer using the methods above)
  Dio get dio => _dio;

  /// Update base URL (useful for different environments)
  void updateBaseUrl(String newBaseUrl) {
    _dio.options.baseUrl = newBaseUrl;
    developer.log(
      'Base URL updated to: $newBaseUrl',
      name: 'ApiClient',
      level: 800,
    );
  }

  /// Test connection to the API
  Future<bool> testConnection() async {
    try {
      final response = await _dio.get('/health');
      return response.statusCode == 200;
    } catch (e) {
      developer.log(
        'Connection test failed: $e',
        name: 'ApiClient',
        level: 900,
      );
      return false;
    }
  }

  /// Get current access token
  Future<String?> getAccessToken() async {
    return await _secureStorage.getAccessToken();
  }

  /// Check if user is authenticated
  Future<bool> isAuthenticated() async {
    return await _secureStorage.isAuthenticated();
  }

  /// Clear all authentication data
  Future<void> clearAuth() async {
    await _secureStorage.clearAll();
    developer.log(
      'Authentication data cleared',
      name: 'ApiClient',
      level: 800,
    );
  }
}