import 'dart:async';

import 'package:flutter/foundation.dart';

import '../media/media_item.dart';
import '../mixins/disposable_change_notifier_mixin.dart';
import '../utils/watch_state_notifier.dart';

@immutable
class WatchStateOverlayPatch {
  final bool? isWatched;
  final bool hasViewOffsetMs;
  final int? viewOffsetMs;

  const WatchStateOverlayPatch({this.isWatched, this.hasViewOffsetMs = false, this.viewOffsetMs});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is WatchStateOverlayPatch &&
          other.isWatched == isWatched &&
          other.hasViewOffsetMs == hasViewOffsetMs &&
          other.viewOffsetMs == viewOffsetMs;

  @override
  int get hashCode => Object.hash(isWatched, hasViewOffsetMs, viewOffsetMs);
}

/// Session-local watch-state overlay for immediate UI freshness.
///
/// Server fetches remain the source of truth; this only patches stale
/// [MediaItem] snapshots while a screen waits for its next refresh.
class WatchStateOverlayProvider extends ChangeNotifier with DisposableChangeNotifierMixin {
  WatchStateOverlayProvider() {
    _subscription = WatchStateNotifier().stream.listen(_onWatchStateEvent);
  }

  StreamSubscription<WatchStateEvent>? _subscription;
  final Map<String, WatchStateOverlayPatch> _patches = {};
  String? _activeProfileId;

  WatchStateOverlayPatch? patchForGlobalKey(String globalKey) => _patches[globalKey];

  WatchStateOverlayPatch? patchForItem(MediaItem item) => patchForGlobalKey(item.globalKey);

  MediaItem apply(MediaItem item) {
    return applyPatch(item, patchForItem(item));
  }

  static MediaItem applyPatch(MediaItem item, WatchStateOverlayPatch? patch) {
    if (patch == null) return item;

    var updated = item;
    final isWatched = patch.isWatched;
    if (isWatched != null) {
      updated = updated.copyWith(viewCount: isWatched ? 1 : 0);
    }
    if (patch.hasViewOffsetMs) {
      updated = updated.copyWith(viewOffsetMs: patch.viewOffsetMs);
    }
    return updated;
  }

  void setActiveProfileId(String? profileId) {
    if (_activeProfileId == profileId) return;
    _activeProfileId = profileId;
    if (_patches.isEmpty) return;
    _patches.clear();
    safeNotifyListeners();
  }

  void _onWatchStateEvent(WatchStateEvent event) {
    final patch = switch (event.changeType) {
      WatchStateChangeType.watched => const WatchStateOverlayPatch(
        isWatched: true,
        hasViewOffsetMs: true,
        viewOffsetMs: 0,
      ),
      WatchStateChangeType.unwatched => const WatchStateOverlayPatch(
        isWatched: false,
        hasViewOffsetMs: true,
        viewOffsetMs: 0,
      ),
      WatchStateChangeType.progressUpdate => WatchStateOverlayPatch(
        hasViewOffsetMs: event.viewOffset != null,
        viewOffsetMs: event.viewOffset,
      ),
      WatchStateChangeType.removedFromContinueWatching => null,
    };

    if (patch == null) return;

    if (_patches[event.globalKey] == patch) return;
    _patches[event.globalKey] = patch;
    safeNotifyListeners();
  }

  @override
  void dispose() {
    _subscription?.cancel();
    _subscription = null;
    super.dispose();
  }
}
