import 'package:http/http.dart' as http;

import '../../features/auth/data/datasources/auth_local_datasource.dart';
import '../../features/auth/domain/repositories/auth_repository.dart';
import '../errors/exceptions.dart';

class AuthenticatedClient extends http.BaseClient {
  final http.Client _inner;
  final AuthLocalDataSource _localDataSource;
  final AuthRepository _authRepository;

  AuthenticatedClient({
    required AuthLocalDataSource localDataSource,
    required AuthRepository authRepository,
    http.Client? inner,
  })  : _localDataSource = localDataSource,
        _authRepository = authRepository,
        _inner = inner ?? http.Client();

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    final tokens = await _localDataSource.getTokens();
    if (tokens != null) {
      request.headers['Authorization'] = 'Bearer ${tokens.accessToken}';
    }

    var response = await _inner.send(request);

    if (response.statusCode == 401 && tokens != null) {
      final refreshResult = await _authRepository.refreshToken();
      final refreshed = refreshResult.when(
        success: (_) => true,
        error: (_) => false,
      );

      if (refreshed) {
        final newTokens = await _localDataSource.getTokens();
        if (newTokens != null) {
          final newRequest = _copyRequest(request, newTokens.accessToken);
          response = await _inner.send(newRequest);
        }
      } else {
        await _localDataSource.clearAll();
        throw const UnauthorizedException(message: 'Session expired');
      }
    }

    return response;
  }

  http.BaseRequest _copyRequest(http.BaseRequest original, String token) {
    final http.Request newRequest = http.Request(original.method, original.url)
      ..headers.addAll(original.headers)
      ..headers['Authorization'] = 'Bearer $token';

    if (original is http.Request && original.body.isNotEmpty) {
      newRequest.body = original.body;
    }

    return newRequest;
  }

  @override
  void close() {
    _inner.close();
    super.close();
  }
}
