import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:rasadyar_core/core.dart';

class MockDio extends Mock implements Dio {}

class MockInterceptor extends Mock implements AppInterceptor {}

class FakeRequestOptions extends Fake implements RequestOptions {}

void main() {
  setUpAll(() {
    registerFallbackValue(FakeRequestOptions());
    registerFallbackValue(RequestOptions(path: ''));
    registerFallbackValue(Options());
    registerFallbackValue(CancelToken());
  });

  group('Dio Remote', () {
    late DioRemote dioRemote;
    late MockDio mockDio;

    setUp(() {
      mockDio = MockDio();
      dioRemote = DioRemote();
      dioRemote.dio = mockDio;
    });

    test('init sets dio and adds interceptor if provided', () async {
      final interceptor = MockInterceptor();

      final client = DioRemote(interceptors: interceptor);
      await client.init();

      expect(client.dio, isA<Dio>());

    });

    test('get returns DioResponse with raw data', () async {
      final response = Response(
        requestOptions: RequestOptions(path: '/test'),
        statusCode: 200,
        data: {'message': 'ok'},
      );
      when(
        () => mockDio.get(
          any(),
          queryParameters: any(named: 'queryParameters'),
          options: any(named: 'options'),
          onReceiveProgress: any(named: 'onReceiveProgress'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.get<Map<String, dynamic>>('/test');

      expect(result, isA<DioResponse<Map<String, dynamic>>>());
      expect(result.data, {'message': 'ok'});
      expect(result.statusCode, 200);
    });

    test('get applies fromJson mapper', () async {
      final response = Response(
        requestOptions: RequestOptions(path: '/user'),
        statusCode: 200,
        data: {'id': 1, 'name': 'Ali'},
      );
      when(
        () => mockDio.get(
          any(),
          queryParameters: any(named: 'queryParameters'),
          options: any(named: 'options'),
          onReceiveProgress: any(named: 'onReceiveProgress'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.get<String>(
        '/user',
        fromJson: (json) => "User: ${json['name']}",
      );

      expect(result.data, 'User: Ali');
    });

    test('post applies fromJson correctly', () async {
      final response = Response(
        requestOptions: RequestOptions(path: '/post'),
        statusCode: 200,
        data: {'id': 99},
      );
      when(
        () => mockDio.post(
          any(),
          data: any(named: 'data'),
          queryParameters: any(named: 'queryParameters'),
          options: any(named: 'options'),
          onSendProgress: any(named: 'onSendProgress'),
          onReceiveProgress: any(named: 'onReceiveProgress'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.post<int>(
        '/post',
        fromJson: (json) => json['id'],
      );

      expect(result.data, 99);
    });

    test('put returns parsed data', () async {
      final response = Response(
        requestOptions: RequestOptions(path: '/put'),
        statusCode: 200,
        data: {'value': 'updated'},
      );
      when(
        () => mockDio.put(
          any(),
          data: any(named: 'data'),
          queryParameters: any(named: 'queryParameters'),
          options: any(named: 'options'),
          onSendProgress: any(named: 'onSendProgress'),
          onReceiveProgress: any(named: 'onReceiveProgress'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.put<String>('/put', fromJson: (json) => json['value']);

      expect(result.data, 'updated');
    });

    test('delete works with fromJson', () async {
      final response = Response(
        requestOptions: RequestOptions(path: '/delete'),
        statusCode: 200,
        data: {'removed': true},
      );
      when(
        () => mockDio.delete(
          any(),
          data: any(named: 'data'),
          queryParameters: any(named: 'queryParameters'),
          options: any(named: 'options'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.delete<bool>('/delete', fromJson: (json) => json['removed']);

      expect(result.data, true);
    });

    test('download returns DioResponse with bytes', () async {
      final response = Response<Uint8List>(
        requestOptions: RequestOptions(path: '/download'),
        statusCode: 200,
        data: Uint8List.fromList([1, 2, 3]),
      );
      when(
        () => mockDio.get<Uint8List>(
          any(),
          options: any(named: 'options'),
          onReceiveProgress: any(named: 'onReceiveProgress'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.download('/download');

      expect(result.data, isA<Uint8List>());
      expect(result.data!.length, 3);
    });

    test('upload sends DioFormData and returns DioResponse', () async {
      final formData = DioFormData();
      formData.addField('field', 'value');
      final response = Response(
        requestOptions: RequestOptions(path: '/upload'),
        statusCode: 200,
        data: 'uploaded',
      );
      when(
        () => mockDio.post(
          any(),
          data: any(named: 'data'),
          options: any(named: 'options'),
          onSendProgress: any(named: 'onSendProgress'),
          cancelToken: any(named: 'cancelToken'),
        ),
      ).thenAnswer((_) async => response);

      final result = await dioRemote.upload<String>('/upload', formData: formData);

      expect(result.data, 'uploaded');
    });
  });
}
