// test/unit/register_use_case_test.dart
//
// Unit test untuk RegisterUseCase.
// Jalankan: flutter test test/unit/register_use_case_test.dart

import 'package:dartz/dartz.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

// ---------- Stubs ----------

abstract class Failure {
  final String message;
  const Failure(this.message);
}

class ServerFailure extends Failure {
  const ServerFailure(super.message);
}

class ValidationFailure extends Failure {
  const ValidationFailure(super.message);
}

class NetworkFailure extends Failure {
  const NetworkFailure(super.message);
}

class UserEntity {
  final String token;
  final String role;
  final String displayName;
  final String? uniqueUserId; // hanya ROLE_USER yang punya
  UserEntity({
    required this.token,
    required this.role,
    required this.displayName,
    this.uniqueUserId,
  });
}

abstract class RegisterRepository {
  Future<Either<Failure, UserEntity>> register({
    required String email,
    required String password,
    required String displayName,
    required String role, // 'ROLE_USER' | 'ROLE_GUARDIAN'
  });
}

class MockRegisterRepository extends Mock implements RegisterRepository {}

// ---------- Use case ----------

class RegisterUseCase {
  final RegisterRepository repository;
  RegisterUseCase(this.repository);

  Future<Either<Failure, UserEntity>> call({
    required String email,
    required String password,
    required String displayName,
    required String role,
  }) async {
    // Validasi email format sederhana
    if (email.trim().isEmpty || !email.contains('@')) {
      return const Left(ValidationFailure('Format email tidak valid'));
    }
    // Validasi password
    if (password.length < 6) {
      return const Left(ValidationFailure('Password harus minimal 6 karakter'));
    }
    // Validasi displayName
    if (displayName.trim().isEmpty) {
      return const Left(ValidationFailure('Nama tidak boleh kosong'));
    }
    // Validasi role
    if (role != 'ROLE_USER' && role != 'ROLE_GUARDIAN') {
      return const Left(ValidationFailure('Role tidak valid'));
    }
    return repository.register(
      email: email.trim(),
      password: password,
      displayName: displayName.trim(),
      role: role,
    );
  }
}

// ---------- Tests ----------

void main() {
  late RegisterUseCase registerUseCase;
  late MockRegisterRepository mockRepo;

  setUp(() {
    mockRepo = MockRegisterRepository();
    registerUseCase = RegisterUseCase(mockRepo);
  });

  group('RegisterUseCase — validasi email', () {
    test('harus gagal jika email kosong', () async {
      final result = await registerUseCase.call(
        email: '',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isLeft(), true);
      result.fold(
        (f) => expect(f, isA<ValidationFailure>()),
        (_) => fail('Seharusnya gagal'),
      );
    });

    test('harus gagal jika email tanpa @', () async {
      final result = await registerUseCase.call(
        email: 'bukan-email',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isLeft(), true);
    });

    test('harus berhasil dengan email valid', () async {
      final fakeUser = UserEntity(
        token: 'tok',
        role: 'ROLE_USER',
        displayName: 'Evan',
        uniqueUserId: 'ABC123DEF456',
      );
      when(mockRepo.register(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      )).thenAnswer((_) async => Right(fakeUser));

      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isRight(), true);
    });
  });

  group('RegisterUseCase — validasi password', () {
    test('harus gagal jika password kurang dari 6 karakter', () async {
      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: '12345',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isLeft(), true);
      result.fold(
        (f) {
          expect(f, isA<ValidationFailure>());
          expect(f.message, contains('6'));
        },
        (_) => fail('Seharusnya gagal'),
      );
    });

    test('harus gagal jika password kosong', () async {
      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: '',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isLeft(), true);
    });

    test('password tepat 6 karakter harus lolos', () async {
      final fakeUser =
          UserEntity(token: 'tok', role: 'ROLE_USER', displayName: 'Evan');
      when(mockRepo.register(
        email: 'evan@test.com',
        password: 'abc123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      )).thenAnswer((_) async => Right(fakeUser));

      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'abc123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isRight(), true);
    });
  });

  group('RegisterUseCase — validasi displayName', () {
    test('harus gagal jika displayName kosong', () async {
      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'password123',
        displayName: '',
        role: 'ROLE_USER',
      );
      expect(result.isLeft(), true);
    });

    test('harus gagal jika displayName hanya spasi', () async {
      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'password123',
        displayName: '   ',
        role: 'ROLE_USER',
      );
      expect(result.isLeft(), true);
    });
  });

  group('RegisterUseCase — validasi role', () {
    test('harus gagal jika role bukan ROLE_USER atau ROLE_GUARDIAN', () async {
      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_ADMIN',
      );
      expect(result.isLeft(), true);
      result.fold(
        (f) => expect(f, isA<ValidationFailure>()),
        (_) => fail('Seharusnya gagal'),
      );
    });

    test('ROLE_USER harus valid', () async {
      final fakeUser = UserEntity(
        token: 'tok',
        role: 'ROLE_USER',
        displayName: 'Evan',
        uniqueUserId: 'ABC123DEF456',
      );
      when(mockRepo.register(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      )).thenAnswer((_) async => Right(fakeUser));

      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      expect(result.isRight(), true);
    });

    test('ROLE_GUARDIAN harus valid', () async {
      final fakeUser = UserEntity(
        token: 'tok',
        role: 'ROLE_GUARDIAN',
        displayName: 'Bambang',
      );
      when(mockRepo.register(
        email: 'bambang@test.com',
        password: 'guardian123',
        displayName: 'Bambang',
        role: 'ROLE_GUARDIAN',
      )).thenAnswer((_) async => Right(fakeUser));

      final result = await registerUseCase.call(
        email: 'bambang@test.com',
        password: 'guardian123',
        displayName: 'Bambang',
        role: 'ROLE_GUARDIAN',
      );
      expect(result.isRight(), true);
    });
  });

  group('RegisterUseCase — ROLE_USER mendapat uniqueUserId', () {
    test('ROLE_USER harus mendapat uniqueUserId dari backend', () async {
      final fakeUser = UserEntity(
        token: 'user_jwt',
        role: 'ROLE_USER',
        displayName: 'Evan',
        uniqueUserId: 'ABC123DEF456',
      );
      when(mockRepo.register(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      )).thenAnswer((_) async => Right(fakeUser));

      final result = await registerUseCase.call(
        email: 'evan@test.com',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );

      result.fold(
        (_) => fail('Seharusnya sukses'),
        (user) {
          expect(user.uniqueUserId, isNotNull);
          expect(user.uniqueUserId!.length, 12);
          expect(user.role, 'ROLE_USER');
        },
      );
    });

    test('ROLE_GUARDIAN tidak perlu uniqueUserId', () async {
      final fakeUser = UserEntity(
        token: 'guardian_jwt',
        role: 'ROLE_GUARDIAN',
        displayName: 'Bambang',
        uniqueUserId: null,
      );
      when(mockRepo.register(
        email: 'bambang@test.com',
        password: 'guardian123',
        displayName: 'Bambang',
        role: 'ROLE_GUARDIAN',
      )).thenAnswer((_) async => Right(fakeUser));

      final result = await registerUseCase.call(
        email: 'bambang@test.com',
        password: 'guardian123',
        displayName: 'Bambang',
        role: 'ROLE_GUARDIAN',
      );

      result.fold(
        (_) => fail('Seharusnya sukses'),
        (user) => expect(user.uniqueUserId, isNull),
      );
    });
  });

  group('RegisterUseCase — error dari server', () {
    test('harus propagate ServerFailure dari repository', () async {
      when(mockRepo.register(
        email: 'taken@test.com',
        password: 'password123',
        displayName: 'Duplicate',
        role: 'ROLE_USER',
      )).thenAnswer(
          (_) async => const Left(ServerFailure('Email sudah terdaftar')));

      final result = await registerUseCase.call(
        email: 'taken@test.com',
        password: 'password123',
        displayName: 'Duplicate',
        role: 'ROLE_USER',
      );

      result.fold(
        (f) {
          expect(f, isA<ServerFailure>());
          expect(f.message, 'Email sudah terdaftar');
        },
        (_) => fail('Seharusnya gagal'),
      );
    });

    test('tidak boleh call repo jika validasi gagal', () async {
      await registerUseCase.call(
        email: '',
        password: 'password123',
        displayName: 'Evan',
        role: 'ROLE_USER',
      );
      verifyNever(mockRepo.register(
        email: 'ignored',
        password: 'ignored',
        displayName: 'ignored',
        role: 'ignored',
      ));
    });
  });
}
