import 'dart:io' show Platform;

import 'package:flutter/services.dart';

import '../media/media_item.dart';
import '../media/media_item_types.dart';
import '../media/media_kind.dart';
import '../media/media_server_client.dart';
import '../utils/app_logger.dart';
import 'settings_service.dart' show EpisodePosterMode;

/// Service for syncing On Deck / Continue Watching content to Android TV's Watch Next row.
class WatchNextService {
  static const MethodChannel _channel = MethodChannel('com.plezy/watch_next');

  static final WatchNextService _instance = WatchNextService._internal();
  factory WatchNextService() => _instance;

  WatchNextService._internal() {
    _channel.setMethodCallHandler(_handleMethodCall);
  }

  /// Callback for when a Watch Next item is tapped (warm start deep link).
  ValueChanged<String>? onWatchNextTap;

  Future<dynamic> _handleMethodCall(MethodCall call) async {
    if (call.method == 'onWatchNextTap') {
      final contentId = call.arguments['contentId'] as String?;
      if (contentId != null) {
        onWatchNextTap?.call(contentId);
      }
    }
  }

  /// Get a pending deep link from cold start (consumed on first call).
  Future<String?> getInitialDeepLink() async {
    if (!Platform.isAndroid) return null;
    try {
      return await _channel.invokeMethod<String>('getInitialDeepLink');
    } catch (e) {
      appLogger.w('Failed to get initial deep link', error: e);
      return null;
    }
  }

  /// Check if Watch Next is supported (Android TV only).
  Future<bool> isSupported() async {
    if (!Platform.isAndroid) return false;
    try {
      return await _channel.invokeMethod<bool>('isSupported') ?? false;
    } catch (e) {
      return false;
    }
  }

  /// Sync On Deck items to Watch Next row.
  Future<bool> syncFromOnDeck(
    List<MediaItem> onDeckItems,
    MediaServerClient Function(String serverId) getClientForServerId, {
    bool hideSpoilers = false,
  }) async {
    if (!Platform.isAndroid) return false;

    try {
      final supported = await isSupported();
      if (!supported) return false;

      final items = onDeckItems.map((item) {
        return _convertToWatchNextItem(item, getClientForServerId, hideSpoilers: hideSpoilers);
      }).toList();

      return await _channel.invokeMethod<bool>('sync', {'items': items}) ?? false;
    } catch (e) {
      appLogger.e('Failed to sync Watch Next', error: e);
      return false;
    }
  }

  /// Clear all Watch Next entries.
  Future<bool> clear() async {
    if (!Platform.isAndroid) return false;
    try {
      return await _channel.invokeMethod<bool>('clear') ?? false;
    } catch (e) {
      appLogger.e('Failed to clear Watch Next', error: e);
      return false;
    }
  }

  /// Remove a single item from Watch Next.
  Future<bool> removeItem(String serverId, String ratingKey) async {
    if (!Platform.isAndroid) return false;
    try {
      final contentId = _buildContentId(serverId, ratingKey);
      return await _channel.invokeMethod<bool>('remove', {'contentId': contentId}) ?? false;
    } catch (e) {
      appLogger.e('Failed to remove Watch Next item', error: e);
      return false;
    }
  }

  /// Build a content ID. Format: plezy_{serverId}_{ratingKey}
  static String _buildContentId(String? serverId, String ratingKey) {
    return 'plezy_${serverId ?? 'unknown'}_$ratingKey';
  }

  /// Parse a content ID back to (serverId, ratingKey), or null if invalid.
  static (String serverId, String ratingKey)? parseContentId(String contentId) {
    if (!contentId.startsWith('plezy_')) return null;
    final parts = contentId.substring(6).split('_');
    if (parts.length < 2) return null;
    return (parts.first, parts.sublist(1).join('_'));
  }

  Map<String, dynamic> _convertToWatchNextItem(
    MediaItem item,
    MediaServerClient Function(String serverId) getClientForServerId, {
    bool hideSpoilers = false,
  }) {
    final contentId = _buildContentId(item.serverId, item.id);

    String? posterUri;
    try {
      if (item.serverId != null) {
        final client = getClientForServerId(item.serverId!);
        String? thumbPath;
        if (hideSpoilers && item.shouldHideSpoiler) {
          thumbPath = item.spoilerSafeArt;
        }
        thumbPath ??= item.posterThumb(mode: EpisodePosterMode.episodeThumbnail, mixedHubContext: true);
        if (thumbPath != null) {
          posterUri = client.thumbnailUrl(thumbPath);
        }
      }
    } catch (e) {
      appLogger.w('Failed to get poster URL for Watch Next: ${item.title}', error: e);
    }

    final String title;
    final String? episodeTitle;
    if (item.kind == MediaKind.episode && item.grandparentTitle != null) {
      title = item.grandparentTitle!;
      episodeTitle = item.title;
    } else {
      title = item.title ?? '';
      episodeTitle = null;
    }

    final lastEngagementTime = item.lastViewedAt != null
        ? item.lastViewedAt! * 1000
        : DateTime.now().millisecondsSinceEpoch;

    return {
      'contentId': contentId,
      'title': title,
      'episodeTitle': episodeTitle,
      'description': item.summary,
      'posterUri': posterUri,
      'type': item.kind.name,
      'duration': item.durationMs ?? 0,
      'lastPlaybackPosition': item.viewOffsetMs ?? 0,
      'lastEngagementTime': lastEngagementTime,
      'seriesTitle': item.grandparentTitle,
      'seasonNumber': item.parentIndex,
      'episodeNumber': item.index,
    };
  }
}
