import 'package:flutter_test/flutter_test.dart';
import 'package:smplayer/core/contracts/library.dart';
import 'package:smplayer/core/infra/emby_api/playback_resolver.dart';

void main() {
  const baseUrl = 'https://emby.example.com';
  const token = 'tok123';
  const itemId = 'item1';
  const mediaSourceId = 'src9';

  EmbyRawMediaStream embeddedSubtitle({
    int index = 2,
    String codec = 'subrip',
    String? title,
    String? language,
    bool isDefault = false,
    bool isForced = false,
  }) => EmbyRawMediaStream(
    Index: index,
    Type: 'Subtitle',
    Codec: codec,
    Title: title,
    Language: language,
    IsDefault: isDefault,
    IsForced: isForced,
  );

  EmbyRawMediaStream externalSubtitle({
    int index = 10,
    String codec = 'srt',
    String? deliveryUrl = '/Videos/item1/10/Subtitles/stream.srt',
    String? deliveryMethod = 'External',
    bool isExternal = true,
    String? title,
    String? language,
  }) => EmbyRawMediaStream(
    Index: index,
    Type: 'Subtitle',
    Codec: codec,
    IsExternal: isExternal,
    DeliveryMethod: deliveryMethod,
    DeliveryUrl: deliveryUrl,
    Title: title,
    Language: language,
  );

  group('PlaybackResolver.resolveSubtitles 外挂字幕', () {
    test('内封轨保留且 isExternal=false（未传 itemId 时无抽取 URL）', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [embeddedSubtitle(index: 2, title: '简中', language: 'chi')],
        baseUrl: baseUrl,
        token: token,
      );
      expect(subtitles, hasLength(1));
      expect(subtitles.single.index, 2);
      expect(subtitles.single.isExternal, isFalse);
      expect(subtitles.single.deliveryUrl, isNull);
      expect(subtitles.single.codec, 'subrip');
    });

    test('外挂文本轨保留，deliveryUrl 补全 base 与 api_key', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [externalSubtitle(index: 10, codec: 'srt', language: 'chi')],
        baseUrl: baseUrl,
        token: token,
      );
      expect(subtitles, hasLength(1));
      final track = subtitles.single;
      expect(track.isExternal, isTrue);
      expect(
        track.deliveryUrl,
        '$baseUrl/Videos/item1/10/Subtitles/stream.srt?api_key=$token',
      );
    });

    test('deliveryUrl 已是绝对地址时不重复补 base，只补 api_key', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [
          externalSubtitle(
            deliveryUrl: 'https://cdn.example.com/subs/a.ass',
            codec: 'ass',
          ),
        ],
        baseUrl: baseUrl,
        token: token,
      );
      expect(
        subtitles.single.deliveryUrl,
        'https://cdn.example.com/subs/a.ass?api_key=$token',
      );
    });

    test('外挂位图轨（pgs/vobsub/dvdsub）被过滤', () {
      for (final bitmapCodec in ['pgssub', 'pgs', 'dvdsub', 'sup', 'vobsub']) {
        final subtitles = PlaybackResolver.resolveSubtitles(
          [externalSubtitle(codec: bitmapCodec)],
          baseUrl: baseUrl,
          token: token,
        );
        expect(subtitles, isEmpty, reason: 'codec=$bitmapCodec 应被过滤');
      }
    });

    test('内封位图轨保留（由引擎渲染，行为不变）', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [embeddedSubtitle(codec: 'pgssub')],
        baseUrl: baseUrl,
        token: token,
      );
      expect(subtitles, hasLength(1));
      expect(subtitles.single.isExternal, isFalse);
    });

    test('外挂轨缺 DeliveryUrl 时被过滤', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [externalSubtitle(deliveryUrl: null)],
        baseUrl: baseUrl,
        token: token,
      );
      expect(subtitles, isEmpty);
    });

    test('未提供 baseUrl/token 时外挂轨被过滤（内封不受影响）', () {
      final subtitles = PlaybackResolver.resolveSubtitles([
        embeddedSubtitle(index: 2),
        externalSubtitle(index: 10),
      ]);
      expect(subtitles, hasLength(1));
      expect(subtitles.single.index, 2);
    });

    test('仅 DeliveryMethod==External（无 IsExternal 标记）也按外挂处理', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [externalSubtitle(isExternal: false, deliveryMethod: 'External')],
        baseUrl: baseUrl,
        token: token,
      );
      expect(subtitles.single.isExternal, isTrue);
    });

    test('混合列表保持原顺序，各归其类', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [
          embeddedSubtitle(index: 2, codec: 'ass'),
          externalSubtitle(index: 10, codec: 'srt'),
          externalSubtitle(index: 11, codec: 'pgssub'),
          embeddedSubtitle(index: 3, codec: 'subrip'),
        ],
        baseUrl: baseUrl,
        token: token,
      );
      expect(subtitles.map((t) => t.index).toList(), [2, 10, 3]);
      expect(subtitles.map((t) => t.isExternal).toList(), [false, true, false]);
    });
  });

  group('PlaybackResolver.resolveSubtitles 内封文本抽取地址', () {
    test('内封文本轨生成服务端抽取 deliveryUrl（isExternal 仍为 false）', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [embeddedSubtitle(index: 2, codec: 'subrip', language: 'chi')],
        baseUrl: baseUrl,
        token: token,
        itemId: itemId,
        mediaSourceId: mediaSourceId,
      );
      expect(subtitles, hasLength(1));
      final track = subtitles.single;
      expect(track.isExternal, isFalse);


      expect(
        track.deliveryUrl,
        '$baseUrl/Videos/$itemId/$mediaSourceId/Subtitles/2/0/Stream.subrip'
        '?api_key=$token',
      );
    });

    test('扩展名用 codec 原名 passthrough（webvtt 归一化为 vtt）', () {
      final cases = {'subrip': 'subrip', 'ssa': 'ssa', 'webvtt': 'vtt'};
      cases.forEach((codec, ext) {
        final subtitles = PlaybackResolver.resolveSubtitles(
          [embeddedSubtitle(index: 4, codec: codec)],
          baseUrl: baseUrl,
          token: token,
          itemId: itemId,
          mediaSourceId: mediaSourceId,
        );
        expect(
          subtitles.single.deliveryUrl,
          '$baseUrl/Videos/$itemId/$mediaSourceId/Subtitles/4/0/Stream.$ext'
          '?api_key=$token',
          reason: 'codec=$codec 应映射为 .$ext',
        );
      });
    });

    test('srt/ass/vtt codec 直接用作扩展名，含 /0/ 段', () {
      final cases = {'srt': 'srt', 'ass': 'ass', 'vtt': 'vtt'};
      cases.forEach((codec, ext) {
        final subtitles = PlaybackResolver.resolveSubtitles(
          [embeddedSubtitle(index: 5, codec: codec)],
          baseUrl: baseUrl,
          token: token,
          itemId: itemId,
          mediaSourceId: mediaSourceId,
        );
        expect(
          subtitles.single.deliveryUrl,
          endsWith('/Subtitles/5/0/Stream.$ext?api_key=$token'),
        );
      });
    });

    test('内封位图轨不生成抽取 URL（deliveryUrl 为 null）', () {
      for (final bitmapCodec in ['pgssub', 'pgs', 'dvdsub', 'vobsub', 'sup']) {
        final subtitles = PlaybackResolver.resolveSubtitles(
          [embeddedSubtitle(index: 6, codec: bitmapCodec)],
          baseUrl: baseUrl,
          token: token,
          itemId: itemId,
          mediaSourceId: mediaSourceId,
        );
        expect(subtitles, hasLength(1));
        expect(
          subtitles.single.deliveryUrl,
          isNull,
          reason: 'codec=$bitmapCodec 不应生成抽取 URL',
        );
      }
    });

    test('缺 mediaSourceId 时内封文本轨不生成抽取 URL', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [embeddedSubtitle(index: 2, codec: 'subrip')],
        baseUrl: baseUrl,
        token: token,
        itemId: itemId,
      );
      expect(subtitles.single.deliveryUrl, isNull);
    });

    test('外挂轨不受内封抽取逻辑影响，仍用自身 DeliveryUrl', () {
      final subtitles = PlaybackResolver.resolveSubtitles(
        [
          embeddedSubtitle(index: 2, codec: 'subrip'),
          externalSubtitle(index: 10, codec: 'srt'),
        ],
        baseUrl: baseUrl,
        token: token,
        itemId: itemId,
        mediaSourceId: mediaSourceId,
      );
      final embedded = subtitles.firstWhere((t) => !t.isExternal);
      final external = subtitles.firstWhere((t) => t.isExternal);
      expect(
        embedded.deliveryUrl,
        '$baseUrl/Videos/$itemId/$mediaSourceId/Subtitles/2/0/Stream.subrip'
        '?api_key=$token',
      );
      expect(
        external.deliveryUrl,
        '$baseUrl/Videos/item1/10/Subtitles/stream.srt?api_key=$token',
      );
    });
  });

  group('PlaybackResolver.isTextSubtitleCodec', () {
    test('文本 codec 判定大小写不敏感', () {
      for (final textCodec in [
        'srt',
        'SUBRIP',
        'vtt',
        'WebVTT',
        'ass',
        'ssa',
      ]) {
        expect(
          PlaybackResolver.isTextSubtitleCodec(textCodec),
          isTrue,
          reason: 'codec=$textCodec',
        );
      }
    });

    test('位图/未知/null codec 返回 false', () {
      for (final nonText in ['pgssub', 'dvdsub', 'sup', 'unknown', null]) {
        expect(
          PlaybackResolver.isTextSubtitleCodec(nonText),
          isFalse,
          reason: 'codec=$nonText',
        );
      }
    });
  });
}
