import 'package:flutter_test/flutter_test.dart';
import 'package:smplayer/core/media/embedded_subtitle_cues.dart';
import 'package:smplayer/core/media/external_subtitle_loader.dart';

void main() {
  group('ExternalSubtitleLoader.parseSubtitleContent — SRT', () {
    const srt = '''
1
00:00:01,000 --> 00:00:02,500
第一句
第二行

2
00:00:04,000 --> 00:00:06,000
<i>Second cue</i>
''';

    test('解析编号块、时间轴与多行文本，剥 HTML 标签', () {
      final cues = ExternalSubtitleLoader.parseSubtitleContent(srt);
      expect(cues, hasLength(2));
      expect(cues[0].start, const Duration(seconds: 1));
      expect(cues[0].end, const Duration(milliseconds: 2500));
      expect(cues[0].text, '第一句\n第二行');
      expect(cues[1].text, 'Second cue');
    });

    test('容忍 CRLF 换行与点号毫秒分隔', () {
      const crlfSrt = '1\r\n00:00:01.000 --> 00:00:02.000\r\nhello\r\n\r\n';
      final cues = ExternalSubtitleLoader.parseSubtitleContent(crlfSrt);
      expect(cues, hasLength(1));
      expect(cues.single.text, 'hello');
    });

    test('无时间轴的垃圾块被跳过', () {
      const noisy = '''
garbage block

1
00:00:01,000 --> 00:00:02,000
ok
''';
      final cues = ExternalSubtitleLoader.parseSubtitleContent(noisy);
      expect(cues, hasLength(1));
      expect(cues.single.text, 'ok');
    });

    test('end < start 的非法 cue 被丢弃', () {
      const invalid = '''
1
00:00:05,000 --> 00:00:02,000
bad

2
00:00:06,000 --> 00:00:07,000
good
''';
      final cues = ExternalSubtitleLoader.parseSubtitleContent(invalid);
      expect(cues, hasLength(1));
      expect(cues.single.text, 'good');
    });
  });

  group('ExternalSubtitleLoader.parseSubtitleContent — VTT', () {
    const vtt = '''
WEBVTT

NOTE this is a comment

cue-1
00:01.000 --> 00:02.000
Hello <b>world</b>

00:00:03.000 --> 00:00:04.500
第二条
''';

    test('识别 WEBVTT 头，支持省略小时位与 cue 标识行', () {
      final cues = ExternalSubtitleLoader.parseSubtitleContent(vtt);
      expect(cues, hasLength(2));
      expect(cues[0].start, const Duration(seconds: 1));
      expect(cues[0].text, 'Hello world');
      expect(cues[1].start, const Duration(seconds: 3));
      expect(cues[1].end, const Duration(milliseconds: 4500));
      expect(cues[1].text, '第二条');
    });
  });

  group('ExternalSubtitleLoader.parseSubtitleContent — ASS 降级纯文本', () {
    const ass = '''
[Script Info]
Title: Test

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:02.50,Default,,0,0,0,,{\\an8\\fs20}你好,世界\\N第二行
Dialogue: 0,0:00:04.00,0:00:05.00,Sign,,0,0,0,,{\\pos(1,2)}m 0 0 l 100 0
Dialogue: 0,0:00:06.00,0:00:07.00,Default,,0,0,0,,plain text
''';

    test('剥样式覆盖标签、\\N 转行、Text 内逗号保留', () {
      final cues = ExternalSubtitleLoader.parseSubtitleContent(ass);
      final first = cues.first;
      expect(first.start, const Duration(seconds: 1));
      expect(first.end, const Duration(milliseconds: 2500));
      expect(first.text, '你好,世界\n第二行');
      expect(first.text, isNot(contains('an8')));
      expect(cues.last.text, 'plain text');
    });

    test('自定义 Format 字段顺序按声明解析', () {
      const customFormat = '''
[Events]
Format: Start, End, Text
Dialogue: 0:00:01.00,0:00:02.00,custom order
''';
      final cues = ExternalSubtitleLoader.parseSubtitleContent(customFormat);
      expect(cues, hasLength(1));
      expect(cues.single.start, const Duration(seconds: 1));
      expect(cues.single.text, 'custom order');
    });

    test('非法时间戳的 Dialogue 行被跳过', () {
      const invalidTime = '''
[Events]
Dialogue: 0,bad,0:00:02.00,Default,,0,0,0,,skip me
Dialogue: 0,0:00:03.00,0:00:04.00,Default,,0,0,0,,keep me
''';
      final cues = ExternalSubtitleLoader.parseSubtitleContent(invalidTime);
      expect(cues, hasLength(1));
      expect(cues.single.text, 'keep me');
    });
  });

  group('parseSubtitleContent — 容错', () {
    test('空内容/纯垃圾返回空列表而非抛异常', () {
      expect(ExternalSubtitleLoader.parseSubtitleContent(''), isEmpty);
      expect(
        ExternalSubtitleLoader.parseSubtitleContent('random garbage\nfoo bar'),
        isEmpty,
      );
    });
  });

  group('SubtitleCueSource.textAt', () {
    SubtitleCueSource sourceFromSrt() => SubtitleCueSource(
      ExternalSubtitleLoader.parseSubtitleContent('''
1
00:00:01,000 --> 00:00:03,000
first

2
00:00:02,000 --> 00:00:04,000
overlap

3
00:00:10,000 --> 00:00:12,000
later
'''),
    );

    test('位置命中单条 cue', () {
      final source = sourceFromSrt();
      expect(source.textAt(const Duration(milliseconds: 1500)), ['first']);
      expect(source.textAt(const Duration(seconds: 11)), ['later']);
    });

    test('重叠 cue 全部返回、按 start 顺序', () {
      final source = sourceFromSrt();
      expect(source.textAt(const Duration(milliseconds: 2500)), [
        'first',
        'overlap',
      ]);
    });

    test('二分跳过历史 cue 时仍保留跨越多个短 cue 的长字幕', () {
      final source = SubtitleCueSource(const [
        TimedSubtitle(
          start: Duration(seconds: 1),
          end: Duration(seconds: 20),
          text: 'long',
        ),
        TimedSubtitle(
          start: Duration(seconds: 2),
          end: Duration(seconds: 3),
          text: 'expired',
        ),
        TimedSubtitle(
          start: Duration(seconds: 10),
          end: Duration(seconds: 12),
          text: 'current',
        ),
      ]);

      expect(source.textAt(const Duration(seconds: 11)), ['long', 'current']);
    });

    test('无命中返回空列表', () {
      final source = sourceFromSrt();
      expect(source.textAt(Duration.zero), isEmpty);
      expect(source.textAt(const Duration(seconds: 5)), isEmpty);
      expect(source.textAt(const Duration(seconds: 20)), isEmpty);
    });
  });
}
