import '../../../../core/errors/exceptions.dart';
import '../../../../core/errors/failures.dart';
import '../../../../core/errors/result.dart';
import '../../../../core/logging/app_logger.dart';
import '../../domain/entities/user_entity.dart';
import '../../domain/repositories/auth_repository.dart';
import '../datasources/auth_local_datasource.dart';
import '../datasources/auth_remote_datasource.dart';

class AuthRepositoryImpl implements AuthRepository {
  final AuthRemoteDataSource remoteDataSource;
  final AuthLocalDataSource localDataSource;
  final AppLogger logger;

  AuthRepositoryImpl({
    required this.remoteDataSource,
    required this.localDataSource,
    required this.logger,
  });

  @override
  Future<Result<UserEntity>> register({
    required String email,
    required String password,
    required String name,
  }) async {
    try {
      final user = await remoteDataSource.register(
        email: email,
        password: password,
        name: name,
      );
      return Success(user);
    } on AuthException catch (e) {
      logger.error('Registration failed: ${e.message}');
      return Error(AuthFailure(message: e.message));
    } on NetworkException {
      logger.error('Network error during registration');
      return const Error(NetworkFailure());
    } on ServerException catch (e) {
      logger.error('Server error during registration: ${e.message}');
      return Error(ServerFailure(message: e.message));
    } catch (e) {
      logger.error('Unexpected error during registration: $e');
      return const Error(UnexpectedFailure());
    }
  }

  @override
  Future<Result<UserEntity>> login({
    required String email,
    required String password,
  }) async {
    try {
      final tokens = await remoteDataSource.login(
        email: email,
        password: password,
      );
      await localDataSource.saveTokens(tokens);

      final user = await remoteDataSource.getCurrentUser(tokens.accessToken);
      await localDataSource.saveUser(user);
      localDataSource.notifyAuthStateChanged(true);

      return Success(user);
    } on AuthException catch (e) {
      logger.error('Login failed: ${e.message}');
      return Error(AuthFailure(message: e.message));
    } on NetworkException {
      logger.error('Network error during login');
      return const Error(NetworkFailure());
    } on ServerException catch (e) {
      logger.error('Server error during login: ${e.message}');
      return Error(ServerFailure(message: e.message));
    } catch (e) {
      logger.error('Unexpected error during login: $e');
      return const Error(UnexpectedFailure());
    }
  }

  @override
  Future<Result<void>> logout() async {
    try {
      await localDataSource.clearAll();
      return const Success(null);
    } catch (e) {
      logger.error('Error during logout: $e');
      return const Error(UnexpectedFailure());
    }
  }

  @override
  Future<Result<UserEntity>> getCurrentUser() async {
    try {
      final cachedUser = await localDataSource.getUser();
      if (cachedUser != null) {
        return Success(cachedUser);
      }

      final tokens = await localDataSource.getTokens();
      if (tokens == null) {
        return const Error(UnauthorizedFailure());
      }

      final user = await remoteDataSource.getCurrentUser(tokens.accessToken);
      await localDataSource.saveUser(user);
      return Success(user);
    } on UnauthorizedException {
      await localDataSource.clearAll();
      return const Error(UnauthorizedFailure());
    } on NetworkException {
      final cachedUser = await localDataSource.getUser();
      if (cachedUser != null) {
        return Success(cachedUser);
      }
      return const Error(NetworkFailure());
    } catch (e) {
      logger.error('Error getting current user: $e');
      return const Error(UnexpectedFailure());
    }
  }

  @override
  Future<Result<void>> refreshToken() async {
    try {
      final tokens = await localDataSource.getTokens();
      if (tokens == null) {
        return const Error(UnauthorizedFailure());
      }

      final newTokens = await remoteDataSource.refreshToken(tokens.refreshToken);
      await localDataSource.saveTokens(newTokens);
      return const Success(null);
    } on UnauthorizedException {
      await localDataSource.clearAll();
      return const Error(UnauthorizedFailure(message: 'Session expired'));
    } on NetworkException {
      return const Error(NetworkFailure());
    } catch (e) {
      logger.error('Error refreshing token: $e');
      return const Error(UnexpectedFailure());
    }
  }

  @override
  Future<bool> isAuthenticated() async {
    final tokens = await localDataSource.getTokens();
    return tokens != null;
  }

  @override
  Stream<bool> get authStateChanges => localDataSource.authStateChanges;
}
