import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:superport_v2/core/network/api_error.dart';

void main() {
  group('ApiErrorMapper', () {
    test('중첩 error 노드에서 메시지와 상세를 추출한다', () {
      final mapper = ApiErrorMapper();
      final requestOptions = RequestOptions(path: '/groups');
      final dioException = DioException(
        requestOptions: requestOptions,
        type: DioExceptionType.badResponse,
        response: Response<dynamic>(
          requestOptions: requestOptions,
          statusCode: 422,
          data: {
            'error': {
              'message': '그룹 이름이 이미 존재합니다.',
              'details': [
                {'field': 'group_name', 'message': '중복된 그룹 이름입니다.'},
              ],
            },
          },
        ),
      );

      final exception = mapper.map(dioException);

      expect(exception.message, '그룹 이름이 이미 존재합니다.');
      expect(exception.statusCode, 422);
      expect(exception.details, isNotNull);
      final details = exception.details!['group_name'];
      if (details is Iterable) {
        expect(details, contains('중복된 그룹 이름입니다.'));
      } else {
        expect(details, '중복된 그룹 이름입니다.');
      }
    });
  });

  test('403 권한 오류 응답에서 세부 정보를 유지한다', () {
    final mapper = ApiErrorMapper();
    final requestOptions = RequestOptions(path: '/stock-transactions/1');
    final dioException = DioException(
      requestOptions: requestOptions,
      type: DioExceptionType.badResponse,
      response: Response<dynamic>(
        requestOptions: requestOptions,
        statusCode: 403,
        data: {
          'error': {
            'code': 'permission_denied',
            'message': '승인 권한이 없습니다.',
            'details': {'action': 'approve'},
            'context': {'resource': 'stock-transactions'},
          },
        },
      ),
    );

    final exception = mapper.map(dioException);

    expect(exception.code, ApiErrorCode.forbidden);
    expect(exception.message, '승인 권한이 없습니다.');
    final details = exception.details!;
    final action = details['action'];
    final resource = details['resource'];

    Iterable<dynamic> asIterable(dynamic value) {
      if (value == null) {
        return const [];
      }
      if (value is Iterable) {
        return value;
      }
      return [value];
    }

    expect(asIterable(action), contains('approve'));
    expect(asIterable(resource), contains('stock-transactions'));
  });

  group('401 응답 메시지 매핑', () {
    test('invalid credentials 메시지를 한글로 변환한다', () {
      final mapper = ApiErrorMapper();
      final requestOptions = RequestOptions(path: '/auth/login');
      final dioException = DioException(
        requestOptions: requestOptions,
        type: DioExceptionType.badResponse,
        response: Response<dynamic>(
          requestOptions: requestOptions,
          statusCode: 401,
          data: {
            'error': {'message': 'invalid credentials'},
          },
        ),
      );

      final exception = mapper.map(dioException);

      expect(exception.code, ApiErrorCode.unauthorized);
      expect(exception.message, '아이디 또는 비밀번호가 올바르지 않습니다.');
    });

    test('token expired 메시지를 세션 만료 문구로 변환하고 상세를 보존한다', () {
      final mapper = ApiErrorMapper();
      final requestOptions = RequestOptions(path: '/auth/refresh');
      final dioException = DioException(
        requestOptions: requestOptions,
        type: DioExceptionType.badResponse,
        response: Response<dynamic>(
          requestOptions: requestOptions,
          statusCode: 401,
          data: {
            'error': {
              'message': 'token expired',
              'details': [
                {'field': 'token', 'message': '만료되었습니다.'},
              ],
            },
          },
        ),
      );

      final exception = mapper.map(dioException);

      expect(exception.message, '세션이 만료되었습니다. 다시 로그인해 주세요.');
      final tokenDetails = exception.details?['token'];
      if (tokenDetails is Iterable) {
        expect(tokenDetails, contains('만료되었습니다.'));
      } else {
        expect(tokenDetails, '만료되었습니다.');
      }
    });
  });
}
