import 'package:dio/dio.dart';
import 'package:http_parser/http_parser.dart';
import '../models/api_error.dart';
import '../blocs/session/session_bloc.dart';
import '../blocs/session/session_event.dart';
import '../blocs/session/session_state.dart';

class ApiClient {
  late final Dio _dio;
  final SessionBloc _sessionBloc;

  ApiClient(this._sessionBloc, {String baseUrl = 'https://go.b0esche.cloud'}) {
    _dio = Dio(
      BaseOptions(
        baseUrl: baseUrl,
        connectTimeout: const Duration(seconds: 10),
        receiveTimeout: const Duration(
          seconds: 60,
        ), // Increased for file uploads and org operations
      ),
    );

    _dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (options, handler) {
          // Add JWT if available
          final token = _getCurrentToken();
          if (token != null) {
            options.headers['Authorization'] = 'Bearer $token';
          }
          return handler.next(options);
        },
        onError: (error, handler) async {
          if (error.response?.statusCode == 401) {
            final path = error.requestOptions.path;
            // Do not expire session for auth endpoints; show inline error instead
            final isAuthEndpoint = path.startsWith('/auth/');
            if (!isAuthEndpoint) {
              // Session expired, trigger logout
              _sessionBloc.add(SessionExpired());
            }
          }
          return handler.next(error);
        },
      ),
    );
  }

  String get baseUrl => _dio.options.baseUrl;

  String? get currentToken => _getCurrentToken();

  String? _getCurrentToken() {
    // Get from SessionBloc state
    final state = _sessionBloc.state;
    if (state is SessionActive) {
      return state.token;
    }
    return null;
  }

  Future<T> get<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    required T Function(dynamic data) fromJson,
  }) async {
    try {
      final response = await _dio.get(path, queryParameters: queryParameters);
      return fromJson(response.data);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<Map<String, dynamic>> getRaw(
    String path, {
    Map<String, dynamic>? queryParameters,
  }) async {
    try {
      final response = await _dio.get(path, queryParameters: queryParameters);
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<T> post<T>(
    String path, {
    dynamic data,
    required T Function(dynamic data) fromJson,
  }) async {
    try {
      final response = await _dio.post(path, data: data);

      return fromJson(response.data);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<List<int>> getBytes(String path) async {
    try {
      final response = await _dio.get(
        path,
        options: Options(responseType: ResponseType.bytes),
      );
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<Map<String, dynamic>> postRaw(String path, {dynamic data}) async {
    try {
      final response = await _dio.post(path, data: data);
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<T> patch<T>(
    String path, {
    dynamic data,
    required T Function(dynamic data) fromJson,
  }) async {
    try {
      final response = await _dio.patch(path, data: data);
      return fromJson(response.data);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<T> put<T>(
    String path, {
    dynamic data,
    required T Function(dynamic data) fromJson,
  }) async {
    try {
      final response = await _dio.put(path, data: data);
      return fromJson(response.data);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<Map<String, dynamic>> putRaw(String path, {dynamic data}) async {
    try {
      final response = await _dio.put(path, data: data);
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<void> delete(String path) async {
    try {
      await _dio.delete(path);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<List<T>> getList<T>(
    String path, {
    Map<String, dynamic>? queryParameters,
    required T Function(dynamic data) fromJson,
  }) async {
    try {
      final response = await _dio.get(path, queryParameters: queryParameters);
      final data = response.data;
      if (data == null) return [];
      return (data as List).map(fromJson).toList();
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  // User profile methods
  Future<Map<String, dynamic>> getUserProfile() async {
    return getRaw('/user/profile');
  }

  Future<Map<String, dynamic>> updateUserProfile({
    String? displayName,
    String? email,
    String? avatarUrl,
  }) async {
    final data = <String, dynamic>{};
    if (displayName != null) data['displayName'] = displayName;
    if (email != null) data['email'] = email;
    if (avatarUrl != null) data['avatarUrl'] = avatarUrl;

    return putRaw('/user/profile', data: data);
  }

  Future<Map<String, dynamic>> changePassword({
    required String currentPassword,
    required String newPassword,
  }) async {
    return postRaw(
      '/user/change-password',
      data: {'currentPassword': currentPassword, 'newPassword': newPassword},
    );
  }

  // Avatar upload
  Future<Map<String, dynamic>> uploadAvatar(
    List<int> imageBytes,
    String filename,
  ) async {
    final formData = FormData.fromMap({
      'avatar': MultipartFile.fromBytes(
        imageBytes,
        filename: filename,
        contentType: MediaType('image', filename.split('.').last),
      ),
    });

    try {
      final response = await _dio.post('/user/avatar', data: formData);
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<Map<String, dynamic>> deleteAccount() async {
    try {
      final response = await _dio.delete('/user/account');
      return response.data;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  ApiError _handleError(DioException e) {
    final status = e.response?.statusCode;
    final data = e.response?.data;

    // Handle network errors
    if (e.type == DioExceptionType.connectionError ||
        e.type == DioExceptionType.connectionTimeout ||
        e.type == DioExceptionType.receiveTimeout ||
        e.type == DioExceptionType.sendTimeout) {
      return ApiError(
        code: 'NETWORK_ERROR',
        message: 'Network error. Please check your connection and try again.',
        status: status,
      );
    }

    // Only try to extract code/message if data is a Map
    String code = 'UNKNOWN';
    String message = 'Unknown error';

    if (data is Map<String, dynamic>) {
      code = data['code'] ?? 'UNKNOWN';
      message = data['message'] ?? 'Unknown error';
    } else if (data != null) {
      message = data.toString();
    }

    return ApiError(code: code, message: message, status: status);
  }
}
