/*
 * 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:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.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/utils.dart';
import 'package:splash_video/splash_video_enums.dart';

/// A widget that displays a video splash screen using media_kit
class SplashVideoPlayer extends StatefulWidget {
  /// Creates a VideoSplash widget
  const SplashVideoPlayer({
    super.key,
    required this.source,
    this.controller,
    this.videoConfig,
    this.onSplashDuration,
    this.onVideoError,
    this.backgroundColor,
  });

  /// Optional controller for managing video playback
  final SplashVideoController? controller;

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

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

  /// Callback invoked when the video duration is determined
  final OnSplashDuration? onSplashDuration;

  /// Callback invoked when video fails to load
  final OnVideoError? onVideoError;

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

  @override
  State<SplashVideoPlayer> createState() => _SplashVideoPlayerState();
}

class _SplashVideoPlayerState extends State<SplashVideoPlayer> {
  late Player player;
  late VideoController controller;
  bool isInitialized = false;
  String? errorMessage;

  VideoConfig get videoConfig => widget.videoConfig ?? const VideoConfig();

  @override
  void initState() {
    super.initState();
    _initializePlayer();
  }

  Future<void> _initializePlayer() async {
    try {
      if (kDebugMode) {
        debugPrint('SplashVideoPlayer: Starting initialization...');
        debugPrint('  Source: ${widget.source}');
      }
      
      // Create the Player instance
      player = Player();
      
      if (kDebugMode) {
        debugPrint('  ✓ Player created');
      }

      // Attach player to controller if provided
      widget.controller?.attach(player);

      // Create the VideoController
      controller = VideoController(player);
      
      if (kDebugMode) {
        debugPrint('  ✓ VideoController created');
      }

      // Configure the player
      await player.setVolume(videoConfig.volume);

      // Disable audio track if audio is not enabled
      if (!videoConfig.enableAudio) {
        await player.setAudioTrack(AudioTrack.no());
      }

      // Set loop mode if controller specifies looping
      if (widget.controller?.loopVideo == true) {
        await player.setPlaylistMode(PlaylistMode.single);
      }

      // Load the video from source
      final media = _getMediaFromSource();
      if (kDebugMode) {
        debugPrint('  ✓ Media created, opening...');
      }
      
      await player.open(media, play: false);
      
      if (kDebugMode) {
        debugPrint('  ✓ Media opened');
      }

      // Listen for errors
      player.stream.error.listen((error) {
        if (mounted && error.isNotEmpty) {
          if (kDebugMode) {
            debugPrint('  ✗ Player error: $error');
          }
          setState(() {
            errorMessage = error;
          });
          
          // Call error callback if provided
          widget.onVideoError?.call(error);
        }
      });

      // Listen for when the player is ready and has duration
      player.stream.duration.listen((duration) {
        if (kDebugMode) {
          debugPrint('  Duration update: $duration');
        }
        
        if (duration != Duration.zero && !isInitialized) {
          if (mounted) {
            if (kDebugMode) {
              debugPrint('  ✓ Video initialized with duration: $duration');
            }
            setState(() {
              isInitialized = true;
            });
          }

          // Notify about the duration
          widget.onSplashDuration?.call(duration);

          // Call the initialization callback
          videoConfig.onPlayerInitialized?.call(player);

          // Play immediately if configured
          if (videoConfig.playImmediately) {
            if (kDebugMode) {
              debugPrint('  ▶ Starting playback');
            }
            player.play();
          }
        }
      });

      // Clean up subscription when widget is disposed
      // Store it so we can cancel in dispose
    } catch (e) {
      if (mounted) {
        final error = 'Failed to initialize video: $e';
        if (kDebugMode) {
          debugPrint('  ✗ Initialization failed: $error');
        }
        setState(() {
          errorMessage = error;
        });
        
        // Call error callback if provided
        widget.onVideoError?.call(error);
      }
    }
  }

  @override
  void dispose() {
    // Don't dispose player if it's managed by a controller
    if (widget.controller == null) {
      // Dispose player after a short delay to avoid disposal during navigation
      Future.delayed(const Duration(milliseconds: 150)).then((_) {
        player.dispose();
      });
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // Show error UI if error occurred and no callback provided
    if (errorMessage != null && widget.onVideoError == null) {
      return ColoredBox(
        color: widget.backgroundColor ?? Colors.black,
        child: Center(
          child: Padding(
            padding: const EdgeInsets.all(24.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Icon(
                  Icons.error_outline,
                  color: Colors.red,
                  size: 64,
                ),
                const SizedBox(height: 16),
                const Text(
                  'Video Load Error',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  errorMessage!,
                  style: const TextStyle(
                    color: Colors.white70,
                    fontSize: 14,
                  ),
                  textAlign: TextAlign.center,
                ),
              ],
            ),
          ),
        ),
      );
    }

    return isInitialized
        ? ColoredBox(
            color: widget.backgroundColor ?? Colors.transparent,
            child: videoConfig.useSafeArea
                ? SafeArea(child: Center(child: _buildMediaWidget()))
                : Center(child: _buildMediaWidget()),
          )
        : const SizedBox.shrink();
  }

  Widget _buildMediaWidget() {
    // Use the fit parameter directly - much simpler!
    // The Video widget handles all the sizing logic internally
    return SizedBox.expand(
      child: Video(
        controller: controller,
        controls: NoVideoControls,
        fit: videoConfig.scale.toBoxFit(),
        fill: widget.backgroundColor ?? const Color(0xFF000000),
      ),
    );
  }

  Media _getMediaFromSource() {
    switch (widget.source) {
      case AssetSource assetSource:
        return Media('asset:///${assetSource.path}');
      case DeviceFileSource deviceFileSource:
        return Media(deviceFileSource.file.path);
      case NetworkFileSource networkFileSource:
        final url = networkFileSource.url;
        if (url != null) {
          return Media(url.toString());
        } else {
          throw SplashVideoException(
            message: "URL can't be null when playing a remote video",
          );
        }
      case BytesSource bytesSource:
        // For bytes, we need to write to a temporary file
        // media_kit doesn't support direct byte playback
        final file = File.fromRawPath(bytesSource.bytes);
        return Media(file.path);
    }
  }
}
