/*
 * Copyright (c) 2026 Malloc LLC (malloc.io)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import 'package:media_kit/media_kit.dart';

/// Controller for managing video splash screen playback
///
/// Provides control over video playback and access to the underlying
/// media_kit Player instance.
class SplashVideoController {
  /// Creates a SplashVideoController
  ///
  /// [loopVideo] - When true, video will loop infinitely until manually stopped
  SplashVideoController({
    this.loopVideo = false,
  }) {
    _activeControllers.add(this);
  }

  /// Whether the video should loop infinitely
  ///
  /// When true:
  /// - Video will restart automatically when it ends
  /// - User must call [skip] or [pause] to stop playback
  /// - Cannot use onVideoComplete or nextScreen callbacks
  final bool loopVideo;

  Player? _player;
  bool _isDisposed = false;

  // Track all active controllers for cleanup during hot restart
  static final Set<SplashVideoController> _activeControllers = {};

  /// Disposes all active SplashVideoController instances
  ///
  /// Call this before hot restart to prevent "Callback invoked after it has
  /// been deleted" crashes. This stops all MPV callback activity before the
  /// Dart isolate is destroyed.
  ///
  /// Usage in main.dart:
  /// ```dart
  /// // In a StatefulWidget wrapping your app:
  /// @override
  /// void reassemble() {
  ///   SplashVideoController.disposeAll();
  ///   super.reassemble();
  /// }
  /// ```
  static Future<void> disposeAll() async {
    final controllers = Set<SplashVideoController>.from(_activeControllers);
    for (final controller in controllers) {
      controller.dispose();
    }
    _activeControllers.clear();
  }

  /// Returns the number of active (non-disposed) controllers
  ///
  /// Useful for debugging resource leaks
  static int get activeControllerCount => _activeControllers.length;

  /// Access to the underlying media_kit Player instance
  ///
  /// Provides full control over video playback including:
  /// - Position seeking
  /// - Volume control
  /// - Playback rate
  /// - Stream listeners for state changes
  ///
  /// Throws [StateError] if accessed before initialization
  Player get player {
    if (_player == null) {
      throw StateError(
        'Player not initialized. Controller must be attached to SplashVideo widget.',
      );
    }
    return _player!;
  }

  /// Internal method to attach the Player instance
  void attach(Player player) {
    if (_isDisposed) {
      throw StateError('Cannot attach player to disposed controller');
    }
    _player = player;
  }

  /// Starts or resumes video playback
  Future<void> play() async {
    if (_isDisposed) return;
    await player.play();
  }

  /// Pauses video playback
  Future<void> pause() async {
    if (_isDisposed) return;
    await player.pause();
  }

  /// Completes the video splash immediately
  ///
  /// This will:
  /// - Stop video playback
  /// - Trigger navigation or completion callbacks
  /// - Clean up resources
  Future<void> skip() async {
    if (_isDisposed) return;
    await player.pause();
    // The SplashVideo widget will handle navigation
    _skipRequested = true;
  }

  bool _skipRequested = false;

  /// Whether skip was requested by the user
  bool get skipRequested => _skipRequested;

  /// Disposes the controller and releases resources
  ///
  /// This method pauses the player first to stop MPV callback activity,
  /// then disposes after a short delay. This helps prevent crashes during
  /// hot restart when the Dart isolate is destroyed while MPV's native
  /// thread is still running.
  ///
  /// Should be called when the splash screen is no longer needed.
  void dispose() {
    if (_isDisposed) return;
    _isDisposed = true;
    _activeControllers.remove(this);

    final player = _player;
    if (player != null) {
      // Pause first to stop callback activity before disposing
      player.pause().then((_) {
        // Small delay to let MPV thread settle before disposal
        Future.delayed(const Duration(milliseconds: 50), () {
          player.dispose();
        });
      }).catchError((_) {
        // If pause fails, dispose anyway
        player.dispose();
      });
    }
    _player = null;
  }

  /// Whether the controller has been disposed
  bool get isDisposed => _isDisposed;
}
