import 'dart:io';
import 'dart:ui' as ui;

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

import '../../shared/utils/app_logger.dart';
import 'playlist/cross_server_playlist.dart';
import 'playlist/pending_cross_server_sequence_provider.dart';


class PlayerExitResult {
  final int? positionTicks;
  final String serverId;
  final String itemId;
  final String? mediaSourceId;

  const PlayerExitResult({
    required this.positionTicks,
    required this.serverId,
    required this.itemId,
    required this.mediaSourceId,
  });
}


class DirectStreamLaunchData {
  final String url;
  final Map<String, String> headers;
  final String title;
  final int? durationSeconds;
  final String? seriesName;
  final int? episode;

  const DirectStreamLaunchData({
    required this.url,
    this.headers = const <String, String>{},
    required this.title,
    this.durationSeconds,
    this.seriesName,
    this.episode,
  });
}

class PlayerLauncher {
  static const _tag = 'PlayerLauncher';


  static Future<PlayerExitResult?> launch({
    required BuildContext context,
    required String itemId,
    String? serverId,
    String? mediaSourceId,
    int preferredSubtitleStreamIndex = -1,
    int? preferredAudioStreamIndex,
    int? startPositionTicks,
    bool fromStart = false,
    String? backdropUrl,
    WidgetRef? ref,
    List<CrossServerPlaylistEntry>? crossServerSequence,
    DirectStreamLaunchData? directStream,
  }) async {
    if (crossServerSequence != null && crossServerSequence.isNotEmpty) {
      assert(
        ref != null,
        'PlayerLauncher.launch: ref is required when crossServerSequence is provided',
      );
      ref!.read(pendingCrossServerSequenceProvider.notifier).state =
          List.unmodifiable(crossServerSequence);
    }
    AppLogger.debug(
      _tag,
      'launch serverId=$serverId itemId=$itemId mediaSourceId=$mediaSourceId '
      'subtitleIndex=$preferredSubtitleStreamIndex '
      'audioIndex=$preferredAudioStreamIndex '
      'startPositionTicks=$startPositionTicks '
      'fromStart=$fromStart '
      'hasBackdrop=${backdropUrl?.isNotEmpty ?? false} '
      'directStream=${directStream != null} '
      'crossServerSequence=${crossServerSequence?.length ?? 0}',
    );

    final queryParams = <String, String>{};
    if (serverId != null && serverId.isNotEmpty) {
      queryParams['serverId'] = serverId;
    }
    if (mediaSourceId != null) queryParams['mediaSourceId'] = mediaSourceId;
    if (backdropUrl != null && backdropUrl.isNotEmpty) {
      queryParams['backdropUrl'] = backdropUrl;
    }
    queryParams['subtitleIndex'] = '$preferredSubtitleStreamIndex';
    if (preferredAudioStreamIndex != null) {
      queryParams['audioIndex'] = '$preferredAudioStreamIndex';
    }
    if (fromStart) {
      queryParams['fromStart'] = '1';
    } else if (startPositionTicks != null && startPositionTicks > 0) {
      queryParams['startPositionTicks'] = '$startPositionTicks';
    }
    final uri = Uri(
      path: '/player/$itemId',
      queryParameters: queryParams.isNotEmpty ? queryParams : null,
    );
    AppLogger.debug(_tag, 'navigating to: ${uri.toString()}');

    OverlayEntry? androidLaunchCover;
    void removeAndroidLaunchCover() {
      final launchCover = androidLaunchCover;
      androidLaunchCover = null;
      launchCover?.remove();
    }

    try {
      if (Platform.isAndroid) {
        final playerView = View.of(context);
        androidLaunchCover = _insertAndroidLaunchCover(context);
        if (androidLaunchCover != null) {
          await WidgetsBinding.instance.endOfFrame;
          if (!context.mounted) return null;
        }

        await SystemChrome.setPreferredOrientations(const <DeviceOrientation>[
          DeviceOrientation.landscapeLeft,
          DeviceOrientation.landscapeRight,
        ]);
        if (!context.mounted) return null;

        await _waitForAndroidLandscape(playerView);
        if (!context.mounted) return null;
      }

      final playerResult = context.push<PlayerExitResult>(
        uri.toString(),
        extra: directStream,
      );
      if (androidLaunchCover != null) {
        await WidgetsBinding.instance.endOfFrame;
        removeAndroidLaunchCover();
      }
      return await playerResult;
    } finally {
      removeAndroidLaunchCover();
      if (Platform.isAndroid) {
        await SystemChrome.setPreferredOrientations(
          const <DeviceOrientation>[],
        );
      }
    }
  }

  static OverlayEntry? _insertAndroidLaunchCover(BuildContext context) {
    final overlay = Overlay.maybeOf(context, rootOverlay: true);
    if (overlay == null) return null;

    final launchCover = OverlayEntry(
      builder: (context) => const Positioned.fill(
        child: IgnorePointer(child: ColoredBox(color: Color(0xFF08090C))),
      ),
    );
    overlay.insert(launchCover);
    return launchCover;
  }

  static Future<void> _waitForAndroidLandscape(
    ui.FlutterView playerView,
  ) async {
    const pollInterval = Duration(milliseconds: 16);
    final deadline = DateTime.now().add(const Duration(milliseconds: 500));

    while (true) {
      final windowSize = playerView.physicalSize;
      if (windowSize.width > windowSize.height) return;
      if (DateTime.now().isAfter(deadline)) return;
      await Future<void>.delayed(pollInterval);
    }
  }
}
