import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:smplayer/core/contracts/cancellation.dart';
import 'package:smplayer/core/infra/player_engine/player_engine.dart';
import 'package:smplayer/features/player/session/player_engine_holder.dart';
import 'package:smplayer/features/player/session/player_engine_resolution.dart';

void main() {
  group('PlayerEngineHolder', () {
    test('同 kind 复用：第二次 acquire 不再调 create', () async {
      final holder = PlayerEngineHolder();
      var createCalls = 0;
      _FakeEngine factory(String tag) {
        createCalls++;
        return _FakeEngine(tag);
      }

      final first = await holder.acquire(
        PlayerEngineKind.media3,
        () => factory('a'),
      );
      final second = await holder.acquire(
        PlayerEngineKind.media3,
        () => factory('b'),
      );

      expect(createCalls, 1);
      expect(identical(first, second), isTrue);
      expect((second as _FakeEngine).tag, 'a');
    });

    test('异 kind dispose+create：旧引擎先 dispose 再新建', () async {
      final holder = PlayerEngineHolder();
      final disposeOrder = <String>[];
      final acquireOrder = <String>[];

      final fvp = _FakeEngine(
        'fvp',
        onDispose: () async {
          await Future<void>.delayed(const Duration(milliseconds: 10));
          disposeOrder.add('fvp');
        },
      );
      await holder.acquire(PlayerEngineKind.fvp, () => fvp);
      acquireOrder.add('fvp');

      final media3 = _FakeEngine('media3');
      final second = await holder.acquire(PlayerEngineKind.media3, () {
        acquireOrder.add('media3-create');
        return media3;
      });

      expect(disposeOrder, ['fvp']);
      expect(acquireOrder, ['fvp', 'media3-create']);
      expect(second, media3);
      expect(fvp.disposed, isTrue);
      expect(media3.disposed, isFalse);
    });
  });
}

class _FakeEngine implements PlayerEngine {
  final String tag;
  final Future<void> Function()? onDispose;
  bool disposed = false;
  _FakeEngine(this.tag, {this.onDispose});

  @override
  Future<void> dispose() async {
    if (disposed) return;
    if (onDispose != null) await onDispose!();
    disposed = true;
  }

  @override
  String get displayName => tag;

  @override
  bool get usesPlatformSurface => false;

  @override
  Future<int?> totalRxBytes() async => null;

  @override
  bool isNetworkStreamError(String error) => false;

  @override
  void configureHardwareDecoding(String mode) {}

  @override
  Future<void> open(
    String url, {
    Duration start = Duration.zero,
    bool play = true,
    Map<String, String>? headers,
    CancellationToken? cancel,
  }) async {}

  @override
  Future<void> play() async {}
  @override
  Future<void> pause() async {}
  @override
  Future<void> stop() async {}
  @override
  Future<void> seek(Duration position) async {}
  @override
  Future<void> setRate(double rate) async {}
  @override
  Future<void> setVolume(double volume) async {}
  @override
  Future<void> toggleMute() async {}
  @override
  Future<void> setAudioTrack(AudioTrack track) async {}
  @override
  Future<void> setSubtitleTrack(SubtitleTrack track) async {}

  @override
  void setNativeSubtitleRendering(bool enabled) {}

  @override
  void setSubtitleStyle({
    required double fontSize,
    required double bgOpacity,
    required int position,
  }) {}

  @override
  Widget buildVideoView(BuildContext context, ValueListenable<BoxFit> fit) =>
      const SizedBox.shrink();

  Future<String> getProperty(String name) async => '';

  @override
  PlayerState get state => PlayerState();

  @override
  PlayerStreams get stream => PlayerStreams();

  @override
  ValueListenable<int?> get textureId => ValueNotifier<int?>(null);

  @override
  ValueListenable<int> get textureVersion => ValueNotifier<int>(0);

  @override
  ValueNotifier<ui.Size?> get videoSize => ValueNotifier<ui.Size?>(null);

  @override
  ValueListenable<PlaybackStats?> get debugStats =>
      ValueNotifier<PlaybackStats?>(null);
}
