import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:social_app/features/auth/data/auth_api.dart';
import 'package:social_app/features/auth/data/auth_repository_impl.dart';
import 'package:social_app/features/auth/data/models/signup_request.dart';
import 'package:social_app/features/auth/data/models/login_request.dart';
import 'package:social_app/features/auth/data/models/auth_response.dart';
import 'package:social_app/core/storage/token_storage.dart';
import 'package:social_app/core/api/api_client.dart';

class MockAuthApi extends Mock implements AuthApi {}

class MockTokenStorage extends Mock implements TokenStorage {}

class MockApiClient extends Mock implements ApiClient {}

void main() {
  late AuthRepositoryImpl repository;
  late MockAuthApi mockApi;
  late MockTokenStorage mockStorage;

  setUp(() {
    mockApi = MockAuthApi();
    mockStorage = MockTokenStorage();
    repository = AuthRepositoryImpl(api: mockApi, tokenStorage: mockStorage);
    registerFallbackValue(
      const SignupStartRequest(username: '', email: '', password: ''),
    );
    registerFallbackValue(const LoginRequest(email: '', password: ''));
    registerFallbackValue(const SignupVerifyRequest(email: '', token: ''));
    registerFallbackValue(const SignupResendRequest(email: ''));
    registerFallbackValue(const LogoutRequest(refreshToken: ''));
    registerFallbackValue(const RefreshRequest(refreshToken: ''));
  });

  group('AuthRepositoryImpl', () {
    test('createVerification calls api and returns response', () async {
      when(() => mockApi.createVerification(any())).thenAnswer(
        (_) async =>
            const VerificationCreateResponse(email: 'test@example.com'),
      );

      final result = await repository.createVerification(
        const SignupStartRequest(
          username: 'testuser',
          email: 'test@example.com',
          password: 'password123',
        ),
      );

      expect(result.email, 'test@example.com');
      verify(() => mockApi.createVerification(any())).called(1);
    });

    test('createSession calls api and saves tokens', () async {
      when(() => mockApi.createSession(any())).thenAnswer(
        (_) async => AuthResponse(
          accessToken: 'access_token',
          refreshToken: 'refresh_token',
          expiresIn: 3600,
          tokenType: 'bearer',
          user: const AuthUser(id: '123', email: 'test@example.com'),
        ),
      );
      when(
        () => mockStorage.saveTokens(
          access: any(named: 'access'),
          refresh: any(named: 'refresh'),
        ),
      ).thenAnswer((_) async {});

      final result = await repository.createSession(
        const LoginRequest(email: 'test@example.com', password: 'password123'),
      );

      expect(result.accessToken, 'access_token');
      verify(
        () => mockStorage.saveTokens(
          access: 'access_token',
          refresh: 'refresh_token',
        ),
      ).called(1);
    });

    test(
      'deleteSession calls api with refresh token and clears storage',
      () async {
        when(
          () => mockStorage.getRefreshToken(),
        ).thenAnswer((_) async => 'refresh_token');
        when(() => mockApi.deleteSession(any())).thenAnswer((_) async {});
        when(() => mockStorage.clear()).thenAnswer((_) async {});

        await repository.deleteSession();

        verify(() => mockApi.deleteSession(any())).called(1);
        verify(() => mockStorage.clear()).called(1);
      },
    );

    test('refreshSession saves new tokens', () async {
      when(() => mockApi.refreshSession(any())).thenAnswer(
        (_) async => AuthResponse(
          accessToken: 'new_access',
          refreshToken: 'new_refresh',
          expiresIn: 3600,
          tokenType: 'bearer',
          user: const AuthUser(id: '123', email: 'test@example.com'),
        ),
      );
      when(
        () => mockStorage.saveTokens(
          access: any(named: 'access'),
          refresh: any(named: 'refresh'),
        ),
      ).thenAnswer((_) async {});

      final result = await repository.refreshSession('old_refresh');

      expect(result.accessToken, 'new_access');
      verify(
        () => mockStorage.saveTokens(
          access: 'new_access',
          refresh: 'new_refresh',
        ),
      ).called(1);
    });
  });
}
