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/masters/user/data/repositories/user_repository_remote.dart';
import 'package:superport_v2/features/masters/user/domain/entities/user.dart';

class _MockApiClient extends Mock implements ApiClient {}

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

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

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

  test('목록 조회 시 include=group 파라미터를 전달한다', () async {
    when(
      () => apiClient.get<Map<String, dynamic>>(
        any(),
        query: any(named: 'query'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer(
      (_) async => Response<Map<String, dynamic>>(
        data: {
          'items': [
            {
              'id': 1,
              'employee_id': 'E-001',
              'name': '홍길동',
              'group': {'id': 2, 'group_name': '관리자'},
              'force_password_change': false,
              'password_updated_at': '2025-01-10T09:00:00Z',
            },
          ],
        },
        requestOptions: RequestOptions(path: '/api/v1/users'),
        statusCode: 200,
      ),
    );

    await repository.list();

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

    expect(path, equals('/api/v1/users'));
    expect(query['include'], 'group');
  });

  test('create 호출 시 employee_id 파라미터를 전달한다', () async {
    final response = Response<Map<String, dynamic>>(
      data: {
        'data': {
          'id': 10,
          'employee_id': 'E2025001',
          'name': '김승인',
          'email': 'approver@example.com',
          'phone': '+82-10-1111-2222',
          'group': {'id': 1, 'group_name': '관리자'},
          'force_password_change': true,
          'is_active': true,
        },
      },
      requestOptions: RequestOptions(path: '/api/v1/users'),
      statusCode: 201,
    );

    when(
      () => apiClient.post<Map<String, dynamic>>(
        any(),
        data: any(named: 'data'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer((_) async => response);
    when(
      () => apiClient.unwrapAsMap(response),
    ).thenReturn(response.data!['data'] as Map<String, dynamic>);

    final input = UserInput(
      employeeNo: 'E2025001',
      employeeName: '김승인',
      groupId: 1,
      email: 'approver@example.com',
      mobileNo: '+82-10-1111-2222',
      password: 'TempPass!1',
    );

    final user = await repository.create(input);

    expect(user.employeeNo, 'E2025001');
    expect(user.forcePasswordChange, isTrue);

    verify(
      () => apiClient.post<Map<String, dynamic>>(
        '/api/v1/users',
        data: any(named: 'data'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).called(1);
  });

  test('updateMe 호출 시 /users/me 엔드포인트를 사용한다', () async {
    final response = Response<Map<String, dynamic>>(
      data: {
        'data': {
          'id': 7,
          'employee_id': 'E2025001',
          'name': '김승인',
          'email': 'approver@example.com',
          'phone': '+82-10-1111-2222',
          'force_password_change': false,
        },
      },
      requestOptions: RequestOptions(path: '/api/v1/users/me'),
      statusCode: 200,
    );

    when(
      () => apiClient.patch<Map<String, dynamic>>(
        any(),
        data: any(named: 'data'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer((_) async => response);
    when(
      () => apiClient.unwrapAsMap(response),
    ).thenReturn(response.data!['data'] as Map<String, dynamic>);

    final payload = UserProfileUpdateInput(
      email: 'approver@example.com',
      phone: '+82-10-1111-2222',
      password: 'NewPass!23',
      currentPassword: 'TempPass!1',
    );

    final user = await repository.updateMe(payload);

    expect(user.email, 'approver@example.com');
    verify(
      () => apiClient.patch<Map<String, dynamic>>(
        '/api/v1/users/me',
        data: payload.toPayload(),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).called(1);
  });

  test('resetPassword 호출 시 /users/{id}/reset-password 엔드포인트를 사용한다', () async {
    final response = Response<Map<String, dynamic>>(
      data: {
        'data': {
          'id': 7,
          'employee_id': 'E2025001',
          'email': 'approver@example.com',
          'force_password_change': true,
          'password_updated_at': '2025-03-11T02:05:00Z',
        },
      },
      requestOptions: RequestOptions(path: '/api/v1/users/7/reset-password'),
      statusCode: 200,
    );

    when(
      () => apiClient.post<Map<String, dynamic>>(
        any(),
        data: any(named: 'data'),
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).thenAnswer((_) async => response);
    when(
      () => apiClient.unwrapAsMap(response),
    ).thenReturn(response.data!['data'] as Map<String, dynamic>);

    final user = await repository.resetPassword(7);

    expect(user.forcePasswordChange, isTrue);
    expect(user.passwordUpdatedAt, isNotNull);
    verify(
      () => apiClient.post<Map<String, dynamic>>(
        '/api/v1/users/7/reset-password',
        data: null,
        options: any(named: 'options'),
        cancelToken: any(named: 'cancelToken'),
      ),
    ).called(1);
  });
}
