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

import 'package:superport_v2/core/network/api_client.dart';
import 'package:superport_v2/features/approvals/data/repositories/approval_draft_repository_remote.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval_draft.dart';

class _MockApiClient extends Mock implements ApiClient {}

void main() {
  late ApiClient apiClient;
  late ApprovalDraftRepositoryRemote repository;

  setUpAll(() {
    registerFallbackValue(Options());
    registerFallbackValue(CancelToken());
    registerFallbackValue(
      Response<dynamic>(requestOptions: RequestOptions(path: '/')),
    );
  });

  setUp(() {
    apiClient = _MockApiClient();
    repository = ApprovalDraftRepositoryRemote(apiClient: apiClient);
  });

  test('list는 requester_id와 include_expired 플래그를 전달한다', () async {
    const path = '/api/v1/approval-drafts';
    when(
      () => apiClient.get<Map<String, dynamic>>(
        path,
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => Response<Map<String, dynamic>>(
        data: {'items': const [], 'page': 1, 'page_size': 20, 'total': 0},
        statusCode: 200,
        requestOptions: RequestOptions(path: path),
      ),
    );

    await repository.list(
      const ApprovalDraftListFilter(requesterId: 7, includeExpired: true),
    );

    final captured = verify(
      () => apiClient.get<Map<String, dynamic>>(
        captureAny(),
        query: captureAny(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).captured;

    expect(captured.first, equals(path));
    final query = captured[1] as Map<String, dynamic>;
    expect(query['requester_id'], 7);
    expect(query['include_expired'], isTrue);
  });

  test('fetch는 requester_id 쿼리로 상세를 조회한다', () async {
    const id = 11;
    const path = '/api/v1/approval-drafts/$id';
    when(
      () => apiClient.get<Map<String, dynamic>>(
        path,
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => Response<Map<String, dynamic>>(
        data: {
          'data': {
            'id': id,
            'requester_id': 5,
            'saved_at': '2025-01-01T00:00:00Z',
            'payload': {'steps': const []},
          },
        },
        statusCode: 200,
        requestOptions: RequestOptions(path: path),
      ),
    );

    final detail = await repository.fetch(id: id, requesterId: 5);

    expect(detail, isNotNull);
    expect(detail!.id, id);

    final captured = verify(
      () => apiClient.get<Map<String, dynamic>>(
        captureAny(),
        query: captureAny(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).captured;
    expect(captured.first, equals(path));
    final query = captured[1] as Map<String, dynamic>;
    expect(query['requester_id'], 5);
  });

  test('save는 초안 상세를 반환한다', () async {
    const path = '/api/v1/approval-drafts';
    when(
      () => apiClient.post<Map<String, dynamic>>(
        path,
        data: any(named: 'data'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => Response<Map<String, dynamic>>(
        data: {
          'data': {
            'id': 22,
            'requester_id': 9,
            'saved_at': '2025-01-02T00:00:00Z',
            'payload': {
              'steps': const [
                {'step_order': 1, 'approver_id': 30},
              ],
            },
          },
        },
        statusCode: 200,
        requestOptions: RequestOptions(path: path),
      ),
    );

    final input = ApprovalDraftSaveInput(
      requesterId: 9,
      steps: [ApprovalDraftStep(stepOrder: 1, approverId: 30)],
    );

    final detail = await repository.save(input);

    expect(detail.id, 22);
    expect(detail.requesterId, 9);

    final captured = verify(
      () => apiClient.post<Map<String, dynamic>>(
        captureAny(),
        data: captureAny(named: 'data'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).captured;
    expect(captured.first, equals(path));
    final payload = captured[1] as Map<String, dynamic>;
    expect(payload['requester_id'], 9);
    expect(payload['steps'], hasLength(1));
  });

  test('delete는 requester_id를 포함해 호출한다', () async {
    const id = 44;
    const path = '/api/v1/approval-drafts/$id';
    when(
      () => apiClient.delete<void>(
        path,
        data: any(named: 'data'),
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => Response<void>(
        data: null,
        statusCode: 204,
        requestOptions: RequestOptions(path: path),
      ),
    );

    await repository.delete(id: id, requesterId: 5);

    final captured = verify(
      () => apiClient.delete<void>(
        captureAny(),
        data: any(named: 'data'),
        query: captureAny(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).captured;
    expect(captured.first, equals(path));
    final query = captured[1] as Map<String, dynamic>;
    expect(query['requester_id'], 5);
  });
}
