/*
 * Copyright (c) 2025
 *
 * 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 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:splash_video/utils.dart';
import 'package:splash_video/video_config.dart';
import 'package:splash_video/splash_video_controller.dart';
import 'package:splash_video/video_source.dart';
import 'package:splash_video/widgets/splash_video_player_w.dart';

/// Main widget for displaying a video splash screen
///
/// This widget handles video playback, overlays, navigation, and lifecycle management.
/// It provides a smooth transition from native splash screen to video splash.
class SplashVideo extends StatefulWidget {
  /// Creates a SplashVideo widget
  ///
  /// [source] is required and specifies the video source
  /// [controller] is required when using looping videos
  /// 
  /// Throws [AssertionError] if validation rules are violated
  const SplashVideo({
    super.key,
    required this.source,
    this.controller,
    this.videoConfig,
    this.backgroundColor,
    this.titleWidget,
    this.footerWidget,
    this.overlayBuilder,
    this.nextScreen,
    this.onVideoComplete,
    this.onSourceLoaded,
    this.onVideoError,
  });

  /// Source for the video (asset, network, file, or bytes)
  final Source source;

  /// Controller for managing video playback
  ///
  /// Required when [controller.loopVideo] is true
  final SplashVideoController? controller;

  /// Configuration options for video playback
  final VideoConfig? videoConfig;

  /// Background color displayed behind the video
  final Color? backgroundColor;

  /// Widget displayed at the top of the screen over the video
  final Widget? titleWidget;

  /// Widget displayed at the bottom of the screen over the video
  final Widget? footerWidget;

  /// Custom overlay builder for full control over UI elements
  ///
  /// This is rendered last and provides complete flexibility
  final Widget Function(BuildContext)? overlayBuilder;

  /// Screen to navigate to when video completes (auto-navigation)
  ///
  /// Cannot be used with [onVideoComplete] or looping videos
  final Widget? nextScreen;

  /// Callback invoked when video completes (manual control)
  ///
  /// Takes priority over [nextScreen]. Cannot be used with looping videos
  final VoidCallback? onVideoComplete;

  /// Callback invoked when video source is loaded
  ///
  /// Defaults to calling [resume] to allow Flutter frames to render
  final VoidCallback? onSourceLoaded;

  /// Callback invoked when video fails to load
  ///
  /// If not provided, a default error UI will be shown
  final OnVideoError? onVideoError;

  // Track whether the first frame has been deferred
  static bool _firstFrameDeferred = false;

  /// Initializes MediaKit and prevents Flutter frames from rendering
  ///
  /// Call this in main() before runApp to:
  /// 1. Defer first frame ASAP to prevent jank
  /// 2. Initialize the MediaKit library for video playback
  /// 3. Keep the native splash visible while video loads
  ///
  /// ⚠️ IMPORTANT: Only call this if your app's FIRST screen (MaterialApp.home)
  /// is a SplashVideoPage. If you show a menu/selector first, DON'T call this.
  ///
  /// ```dart
  /// void main() {
  ///   WidgetsFlutterBinding.ensureInitialized();
  ///   SplashVideoPage.initialize(); // Only if first screen is SplashVideoPage!
  ///   runApp(MyApp());
  /// }
  /// ```
  static void initialize() {
    // Defer first frame ASAP to avoid jank
    WidgetsBinding.instance.deferFirstFrame();
    _firstFrameDeferred = true;
    
    // Then initialize MediaKit
    MediaKit.ensureInitialized();
    
    if (kDebugMode) {
      debugPrint('✓ SplashVideo: First frame deferred, MediaKit initialized');
      debugPrint('⚠️  Waiting for SplashVideoPage to resume rendering...');
      debugPrint('⚠️  If your app shows a black screen, make sure SplashVideoPage is your FIRST screen!');
    }
  }

  /// Tests if MediaKit is properly configured and can create a Player
  ///
  /// Returns true if MediaKit is working, false otherwise.
  /// Prints diagnostic information in debug mode.
  static Future<bool> testMediaKit() async {
    try {
      if (kDebugMode) {
        debugPrint('Testing MediaKit installation...');
      }
      
      // Initialize MediaKit if not already done
      MediaKit.ensureInitialized();
      
      // Try to create and dispose a player
      final testPlayer = Player();
      await testPlayer.dispose();
      
      if (kDebugMode) {
        debugPrint('✓ MediaKit test passed: Player created and disposed successfully');
      }
      return true;
    } catch (e) {
      if (kDebugMode) {
        debugPrint('✗ MediaKit test failed: $e');
        debugPrint('  Make sure media_kit platform libraries are added to pubspec.yaml');
      }
      return false;
    }
  }

  /// Resumes Flutter frame rendering
  ///
  /// Called automatically by default when video loads, or manually
  /// through [onSourceLoaded] callback
  /// 
  /// Only calls allowFirstFrame() if the first frame was actually deferred
  static void resume() {
    if (_firstFrameDeferred) {
      WidgetsBinding.instance.allowFirstFrame();
      _firstFrameDeferred = false;
      
      if (kDebugMode) {
        debugPrint('✓ SplashVideo: First frame resumed, app is now visible');
      }
    }
  }

  @override
  State<SplashVideo> createState() => _SplashVideoState();
}

class _SplashVideoState extends State<SplashVideo> {
  Timer? _completionTimer;
  StreamSubscription? _skipSubscription;

  late final VoidCallback onSourceLoaded;

  @override
  void initState() {
    super.initState();
    
    // Validate configuration
    _validateConfiguration();
    
    onSourceLoaded = widget.onSourceLoaded ?? SplashVideo.resume;
    
    // Listen for skip requests if controller provided
    if (widget.controller != null) {
      _listenForSkip();
    }
  }

  void _validateConfiguration() {
    // Cannot use both nextScreen and onVideoComplete
    if (widget.nextScreen != null && widget.onVideoComplete != null) {
      throw ArgumentError(
        'Cannot use both nextScreen and onVideoComplete. '
        'Use onVideoComplete for manual control or nextScreen for auto-navigation.',
      );
    }

    // Looping videos need controller
    if (widget.controller?.loopVideo == true) {
      // Cannot use nextScreen with looping
      if (widget.nextScreen != null) {
        throw ArgumentError(
          'Cannot use nextScreen with looping videos. '
          'Use controller.skip() to end the video manually.',
        );
      }
      // Cannot use onVideoComplete with looping
      if (widget.onVideoComplete != null) {
        throw ArgumentError(
          'Cannot use onVideoComplete with looping videos. '
          'Use controller.skip() to end the video manually.',
        );
      }
    }
  }

  void _listenForSkip() {
    // Poll for skip requests
    _skipSubscription = Stream.periodic(
      const Duration(milliseconds: 100),
    ).listen((_) {
      if (widget.controller?.skipRequested == true) {
        _onSplashComplete();
      }
    });
  }

  @override
  void dispose() {
    _completionTimer?.cancel();
    _skipSubscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return _buildVideoWithOverlays();
  }

  Widget _buildVideoWithOverlays() {
    final videoWidget = SplashVideoPlayer(
      source: widget.source,
      controller: widget.controller,
      videoConfig: widget.videoConfig,
      backgroundColor: widget.backgroundColor,
      onSplashDuration: _updateSplashDuration,
      onVideoError: widget.onVideoError,
    );

    // If no overlays, return video directly
    if (widget.titleWidget == null &&
        widget.footerWidget == null &&
        widget.overlayBuilder == null) {
      return videoWidget;
    }

    // Build stack with overlays
    return Stack(
      children: [
        videoWidget,
        if (widget.titleWidget != null)
          Positioned(
            top: 0,
            left: 0,
            right: 0,
            child: SafeArea(child: widget.titleWidget!),
          ),
        if (widget.footerWidget != null)
          Positioned(
            bottom: 0,
            left: 0,
            right: 0,
            child: SafeArea(child: widget.footerWidget!),
          ),
        if (widget.overlayBuilder != null)
          widget.overlayBuilder!(context),
      ],
    );
  }

  void _updateSplashDuration(Duration duration) {
    if (kDebugMode) {
      debugPrint('SplashVideoPage: Video loaded with duration $duration');
      debugPrint('  Calling onSourceLoaded to resume first frame...');
    }
    
    // Call the onSourceLoaded callback
    onSourceLoaded.call();
    
    if (kDebugMode) {
      debugPrint('  ✓ First frame resumed');
    }

    // Don't set timer for looping videos
    if (widget.controller?.loopVideo == true) {
      return;
    }

    // Set timer to complete after video duration
    _completionTimer?.cancel();
    _completionTimer = Timer(duration, _onSplashComplete);
  }

  void _onSplashComplete() {
    // Prevent multiple calls
    if (!mounted) return;

    // Priority 1: onVideoComplete callback
    if (widget.onVideoComplete != null) {
      widget.onVideoComplete!.call();
      return;
    }

    // Priority 2: Auto-navigate to nextScreen
    if (widget.nextScreen != null) {
      Navigator.of(context).pushReplacement(
        MaterialPageRoute(
          builder: (_) => widget.nextScreen!,
        ),
      );
      return;
    }

    // No navigation configured, do nothing
  }
}
