import 'dart:io';

import 'package:dio/dio.dart';
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_hive_store/dio_cache_interceptor_hive_store.dart';
import 'package:flutter/foundation.dart';
import 'package:lunchpick/core/utils/app_logger.dart';
import 'package:path_provider/path_provider.dart';

import 'network_config.dart';
import '../errors/network_exceptions.dart';
import 'interceptors/retry_interceptor.dart';
import 'interceptors/logging_interceptor.dart';

/// 네트워크 클라이언트
///
/// Dio를 기반으로 한 중앙화된 HTTP 클라이언트입니다.
/// 재시도, 캐싱, 로깅 등의 기능을 제공합니다.
class NetworkClient {
  late final Dio _dio;
  CacheStore? _cacheStore;

  NetworkClient() {
    _dio = Dio(_createBaseOptions());
    _setupInterceptors();
  }

  /// 기본 옵션 생성
  BaseOptions _createBaseOptions() {
    return BaseOptions(
      connectTimeout: Duration(milliseconds: NetworkConfig.connectTimeout),
      receiveTimeout: Duration(milliseconds: NetworkConfig.receiveTimeout),
      sendTimeout: Duration(milliseconds: NetworkConfig.sendTimeout),
      headers: {
        'User-Agent': NetworkConfig.userAgent,
        'Accept': 'application/json, text/html, */*',
        'Accept-Language': 'ko-KR,ko;q=0.9,en;q=0.8',
      },
      validateStatus: (status) => status != null && status < 500,
    );
  }

  /// 인터셉터 설정
  Future<void> _setupInterceptors() async {
    // 로깅 인터셉터 (디버그 모드에서만)
    if (kDebugMode) {
      _dio.interceptors.add(LoggingInterceptor());
    }

    // 재시도 인터셉터
    _dio.interceptors.add(RetryInterceptor(dio: _dio));

    // 캐시 인터셉터 설정
    await _setupCacheInterceptor();

    // 에러 변환 인터셉터
    _dio.interceptors.add(
      InterceptorsWrapper(
        onError: (error, handler) {
          handler.next(_transformError(error));
        },
      ),
    );
  }

  /// 캐시 인터셉터 설정
  Future<void> _setupCacheInterceptor() async {
    try {
      if (!kIsWeb) {
        final dir = await getTemporaryDirectory();
        final cacheDir = Directory('${dir.path}/lunchpick_cache');

        if (!await cacheDir.exists()) {
          await cacheDir.create(recursive: true);
        }

        _cacheStore = HiveCacheStore(cacheDir.path);
      } else {
        // 웹 환경에서는 메모리 캐시 사용
        _cacheStore = MemCacheStore();
      }

      final cacheOptions = CacheOptions(
        store: _cacheStore,
        policy: CachePolicy.forceCache,
        maxStale: NetworkConfig.cacheMaxAge,
        priority: CachePriority.normal,
        keyBuilder: CacheOptions.defaultCacheKeyBuilder,
        allowPostMethod: false,
      );

      _dio.interceptors.add(DioCacheInterceptor(options: cacheOptions));
    } catch (e, stackTrace) {
      AppLogger.error(
        'NetworkClient: 캐시 설정 실패 - $e',
        error: e,
        stackTrace: stackTrace,
      );
      // 캐시 실패해도 계속 진행
    }
  }

  /// 에러 변환
  DioException _transformError(DioException error) {
    NetworkException networkException;

    switch (error.type) {
      case DioExceptionType.connectionTimeout:
      case DioExceptionType.sendTimeout:
      case DioExceptionType.receiveTimeout:
        networkException = ConnectionTimeoutException(originalError: error);
        break;

      case DioExceptionType.connectionError:
        networkException = NoInternetException(originalError: error);
        break;

      case DioExceptionType.badResponse:
        final statusCode = error.response?.statusCode ?? 0;
        final message = _getErrorMessage(error.response);

        if (statusCode >= 500) {
          networkException = ServerException(
            message: message,
            statusCode: statusCode,
            originalError: error,
          );
        } else if (statusCode >= 400) {
          networkException = ClientException(
            message: message,
            statusCode: statusCode,
            originalError: error,
          );
        } else {
          networkException = ClientException(
            message: message,
            statusCode: statusCode,
            originalError: error,
          );
        }
        break;

      default:
        networkException = NoInternetException(
          message: error.message ?? '알 수 없는 네트워크 오류가 발생했습니다',
          originalError: error,
        );
    }

    return DioException(
      requestOptions: error.requestOptions,
      response: error.response,
      type: error.type,
      error: networkException,
    );
  }

  /// 에러 메시지 추출
  String _getErrorMessage(Response? response) {
    if (response == null) {
      return '서버 응답을 받을 수 없습니다';
    }

    final statusCode = response.statusCode ?? 0;

    // 상태 코드별 기본 메시지
    switch (statusCode) {
      case 400:
        return '잘못된 요청입니다';
      case 401:
        return '인증이 필요합니다';
      case 403:
        return '접근 권한이 없습니다';
      case 404:
        return '요청한 리소스를 찾을 수 없습니다';
      case 429:
        return '너무 많은 요청을 보냈습니다. 잠시 후 다시 시도해주세요';
      case 500:
        return '서버 내부 오류가 발생했습니다';
      case 502:
        return '게이트웨이 오류가 발생했습니다';
      case 503:
        return '서비스를 일시적으로 사용할 수 없습니다';
      default:
        return '서버 오류가 발생했습니다 (HTTP $statusCode)';
    }
  }

  /// GET 요청
  Future<Response<T>> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onReceiveProgress,
    bool useCache = true,
  }) {
    final requestOptions = options ?? Options();

    // 캐시 사용 설정
    if (!useCache) {
      requestOptions.extra = {...?requestOptions.extra, 'disableCache': true};
    }

    return _dio.get<T>(
      path,
      queryParameters: queryParameters,
      options: requestOptions,
      cancelToken: cancelToken,
      onReceiveProgress: onReceiveProgress,
    );
  }

  /// POST 요청
  Future<Response<T>> post<T>(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
    ProgressCallback? onSendProgress,
    ProgressCallback? onReceiveProgress,
  }) {
    return _dio.post<T>(
      path,
      data: data,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
      onSendProgress: onSendProgress,
      onReceiveProgress: onReceiveProgress,
    );
  }

  /// HEAD 요청 (리다이렉션 확인용)
  Future<Response<T>> head<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
    CancelToken? cancelToken,
  }) {
    return _dio.head<T>(
      path,
      queryParameters: queryParameters,
      options: options,
      cancelToken: cancelToken,
    );
  }

  /// 캐시 삭제
  Future<void> clearCache() async {
    await _cacheStore?.clean();
  }

  /// 리소스 정리
  void dispose() {
    _dio.close();
    _cacheStore?.close();
  }
}

/// 기본 네트워크 클라이언트 인스턴스
final networkClient = NetworkClient();
