import 'dart:convert';
import 'dart:typed_data';

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/reporting/data/repositories/reporting_repository_remote.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_export_format.dart';
import 'package:superport_v2/features/reporting/domain/entities/report_export_request.dart';

class _MockApiClient extends Mock implements ApiClient {}

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

  setUpAll(() {
    registerFallbackValue(Options());
    registerFallbackValue(CancelToken());
  });

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

  Response<Uint8List> jsonResponse(String path, Map<String, dynamic> body) {
    final bytes = Uint8List.fromList(jsonEncode(body).codeUnits);
    return Response<Uint8List>(
      data: bytes,
      headers: Headers.fromMap({
        'content-type': ['application/json'],
      }),
      requestOptions: RequestOptions(path: path),
      statusCode: 200,
    );
  }

  Response<Uint8List> binaryResponse(
    String path, {
    required List<int> bytes,
    required String filename,
    required String mimeType,
  }) {
    return Response<Uint8List>(
      data: Uint8List.fromList(bytes),
      headers: Headers.fromMap({
        'content-type': [mimeType],
        'content-disposition': ['attachment; filename="$filename"'],
      }),
      requestOptions: RequestOptions(path: path),
      statusCode: 200,
    );
  }

  test('exportTransactions는 download_url을 파싱한다', () async {
    const path = '/api/v1/reports/transactions/export';
    when(
      () => apiClient.get<Uint8List>(
        path,
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => jsonResponse(path, {
        'data': {
          'download_url': 'https://example.com/report.xlsx',
          'filename': 'report.xlsx',
          'mime_type':
              'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
          'expires_at': '2025-01-01T00:00:00Z',
        },
      }),
    );

    final request = ReportExportRequest(
      from: DateTime(2024, 1, 1),
      to: DateTime(2024, 1, 31),
      format: ReportExportFormat.xlsx,
      transactionStatusId: 3,
      approvalStatusId: 7,
      requestedById: 9,
    );

    final result = await repository.exportTransactions(request);

    final captured = verify(
      () => apiClient.get<Uint8List>(
        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['from'], '2024-01-01');
    expect(query['to'], '2024-01-31');
    expect(query['format'], 'xlsx');
    expect(query['transaction_status_id'], 3);
    expect(query['approval_status_id'], 7);
    expect(query['requested_by_id'], 9);

    expect(result.downloadUrl.toString(), 'https://example.com/report.xlsx');
    expect(result.filename, 'report.xlsx');
    expect(
      result.mimeType,
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    expect(result.expiresAt, DateTime.parse('2025-01-01T00:00:00Z'));
    expect(result.hasDownloadUrl, isTrue);
    expect(result.hasBytes, isFalse);
  });

  test('exportApprovals는 바이너리 응답을 처리한다', () async {
    const path = '/api/v1/reports/approvals/export';
    when(
      () => apiClient.get<Uint8List>(
        path,
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => binaryResponse(
        path,
        bytes: [1, 2, 3],
        filename: 'approval.pdf',
        mimeType: 'application/pdf',
      ),
    );

    final request = ReportExportRequest(
      from: DateTime(2024, 2, 1),
      to: DateTime(2024, 2, 15),
      format: ReportExportFormat.pdf,
      approvalStatusId: 5,
    );

    final result = await repository.exportApprovals(request);

    final captured = verify(
      () => apiClient.get<Uint8List>(
        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['from'], DateTime(2024, 2, 1).toIso8601String());
    expect(query['to'], DateTime(2024, 2, 15).toIso8601String());
    expect(query['format'], 'pdf');
    expect(query.containsKey('transaction_status_id'), isFalse);
    expect(result.hasBytes, isTrue);
    expect(result.bytes, isNotNull);
    expect(result.filename, 'approval.pdf');
    expect(result.mimeType, 'application/pdf');
    expect(result.hasDownloadUrl, isFalse);
  });

  test('exportApprovals는 transaction_status_id 파라미터를 전달한다', () async {
    const path = '/api/v1/reports/approvals/export';
    when(
      () => apiClient.get<Uint8List>(
        path,
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => binaryResponse(
        path,
        bytes: [4, 5, 6],
        filename: 'approval.xlsx',
        mimeType:
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      ),
    );

    final request = ReportExportRequest(
      from: DateTime(2024, 4, 1),
      to: DateTime(2024, 4, 30),
      format: ReportExportFormat.xlsx,
      transactionStatusId: 2,
      approvalStatusId: 4,
    );

    await repository.exportApprovals(request);

    final captured = verify(
      () => apiClient.get<Uint8List>(
        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['transaction_status_id'], 2);
    expect(query['approval_status_id'], 4);
  });
}
