import 'package:web/web.dart' as web;
import 'dart:async';
import 'dart:typed_data';
import '../services/api_client.dart';
import '../injection.dart';

class AudioPlayer {
  web.HTMLAudioElement? _audioElement;
  String? _blobUrl;
  final StreamController<Duration> _positionController =
      StreamController<Duration>.broadcast();
  final StreamController<Duration> _durationController =
      StreamController<Duration>.broadcast();
  final StreamController<bool> _playingController =
      StreamController<bool>.broadcast();
  final StreamController<String> _errorController =
      StreamController<String>.broadcast();

  // Store subscriptions for cleanup
  StreamSubscription? _durationSubscription;
  StreamSubscription? _positionSubscription;
  StreamSubscription? _playSubscription;
  StreamSubscription? _pauseSubscription;
  StreamSubscription? _endedSubscription;
  StreamSubscription? _errorSubscription;

  Stream<Duration> get positionStream => _positionController.stream;
  Stream<Duration> get durationStream => _durationController.stream;
  Stream<bool> get playingStream => _playingController.stream;
  Stream<String> get errorStream => _errorController.stream;

  void _disposeSubscriptions() {
    _durationSubscription?.cancel();
    _positionSubscription?.cancel();
    _playSubscription?.cancel();
    _pauseSubscription?.cancel();
    _endedSubscription?.cancel();
    _errorSubscription?.cancel();

    _durationSubscription = null;
    _positionSubscription = null;
    _playSubscription = null;
    _pauseSubscription = null;
    _endedSubscription = null;
    _errorSubscription = null;
  }

  Future<void> setUrl(String url, {String? mimeType}) async {
    // Clean up any existing subscriptions
    _disposeSubscriptions();

    try {
      final apiClient = getIt<ApiClient>();
      final path = url.replaceFirst(apiClient.baseUrl, '');
      final bytes = await apiClient.getBytes(path);
      final blob = web.Blob(
        [Uint8List.fromList(bytes)] as dynamic,
        web.BlobPropertyBag(type: mimeType ?? 'audio/mpeg'),
      );
      final blobUrl = web.URL.createObjectURL(blob);

      _audioElement = web.HTMLAudioElement();
      _audioElement!.src = blobUrl;
      _audioElement!.crossOrigin = 'anonymous'; // Handle CORS

      // Set up event listeners and store subscriptions
      _durationSubscription = _audioElement!.onLoadedMetadata.listen((_) {
        if (_audioElement != null) {
          _durationController.add(
            Duration(milliseconds: (_audioElement!.duration * 1000).toInt()),
          );
        }
      });

      _positionSubscription = _audioElement!.onTimeUpdate.listen((_) {
        if (_audioElement != null) {
          _positionController.add(
            Duration(milliseconds: (_audioElement!.currentTime * 1000).toInt()),
          );
        }
      });

      _playSubscription = _audioElement!.onPlay.listen((_) {
        _playingController.add(true);
      });

      _pauseSubscription = _audioElement!.onPause.listen((_) {
        _playingController.add(false);
      });

      _endedSubscription = _audioElement!.onEnded.listen((_) {
        _playingController.add(false);
      });

      _errorSubscription = _audioElement!.onError.listen((_) {
        _errorController.add('Failed to load audio');
      });

      // Load the audio
      _audioElement!.load();
    } catch (e) {
      _errorController.add('Error initializing audio: $e');
    }
  }

  Future<void> play() async {
    try {
      if (_audioElement != null) {
        // The play() method returns a JSPromise, but we can call it without await
        // since we're not depending on the promise resolution for our logic
        _audioElement!.play();
      }
    } catch (e) {
      _errorController.add('Error playing audio: $e');
    }
  }

  Future<void> pause() async {
    _audioElement?.pause();
  }

  Future<void> seek(Duration position) async {
    if (_audioElement != null) {
      _audioElement!.currentTime = position.inMilliseconds / 1000;
    }
  }

  void dispose() {
    _disposeSubscriptions();
    _audioElement?.pause();
    if (_blobUrl != null) {
      web.URL.revokeObjectURL(_blobUrl!);
      _blobUrl = null;
    }
    _audioElement = null;
    _positionController.close();
    _durationController.close();
    _playingController.close();
    _errorController.close();
  }
}
