"""Duplication 查重记录领域实体单测."""

import pytest

from packages.domain.duplication import DuplicateSegment, DuplicationRecord


class TestDuplicateSegmentCreate:
    def test_create_normal(self):
        seg = DuplicateSegment.create(
            source_start=10.0,
            source_end=20.0,
            matched_video_id="vid123",
            matched_video_name="测试视频",
            matched_start=5.0,
            matched_end=15.0,
            similarity=85.5,
        )
        assert seg.id
        assert seg.source_start == 10.0
        assert seg.source_end == 20.0
        assert seg.matched_video_id == "vid123"
        assert seg.matched_video_name == "测试视频"
        assert seg.matched_start == 5.0
        assert seg.matched_end == 15.0
        assert seg.similarity == 85.5

    def test_create_negative_source_start_raises(self):
        with pytest.raises(ValueError, match="invalid source segment range"):
            DuplicateSegment.create(
                source_start=-1.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=0.0,
                matched_end=10.0,
                similarity=50.0,
            )

    def test_create_zero_duration_source_raises(self):
        with pytest.raises(ValueError, match="invalid source segment range"):
            DuplicateSegment.create(
                source_start=10.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=0.0,
                matched_end=10.0,
                similarity=50.0,
            )

    def test_create_reversed_source_range_raises(self):
        with pytest.raises(ValueError, match="invalid source segment range"):
            DuplicateSegment.create(
                source_start=20.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=0.0,
                matched_end=10.0,
                similarity=50.0,
            )

    def test_create_negative_matched_start_raises(self):
        with pytest.raises(ValueError, match="invalid matched segment range"):
            DuplicateSegment.create(
                source_start=0.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=-1.0,
                matched_end=10.0,
                similarity=50.0,
            )

    def test_create_zero_duration_matched_raises(self):
        with pytest.raises(ValueError, match="invalid matched segment range"):
            DuplicateSegment.create(
                source_start=0.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=5.0,
                matched_end=5.0,
                similarity=50.0,
            )

    def test_create_similarity_negative_raises(self):
        with pytest.raises(ValueError, match="similarity must be between 0 and 100"):
            DuplicateSegment.create(
                source_start=0.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=0.0,
                matched_end=10.0,
                similarity=-1.0,
            )

    def test_create_similarity_over_100_raises(self):
        with pytest.raises(ValueError, match="similarity must be between 0 and 100"):
            DuplicateSegment.create(
                source_start=0.0,
                source_end=10.0,
                matched_video_id="v1",
                matched_video_name="v",
                matched_start=0.0,
                matched_end=10.0,
                similarity=101.0,
            )

    def test_create_similarity_boundary_values(self):
        # 0 和 100 都是合法的
        seg0 = DuplicateSegment.create(
            source_start=0.0,
            source_end=1.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=1.0,
            similarity=0.0,
        )
        assert seg0.similarity == 0.0

        seg100 = DuplicateSegment.create(
            source_start=0.0,
            source_end=1.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=1.0,
            similarity=100.0,
        )
        assert seg100.similarity == 100.0

    def test_create_unique_ids(self):
        seg1 = DuplicateSegment.create(
            source_start=0.0,
            source_end=1.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=1.0,
            similarity=50.0,
        )
        seg2 = DuplicateSegment.create(
            source_start=0.0,
            source_end=1.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=1.0,
            similarity=50.0,
        )
        assert seg1.id != seg2.id


class TestDuplicationRecordCreate:
    def test_create_normal(self):
        record = DuplicationRecord.create(
            user_id="user1",
            filename="test.mp4",
            file_size=1024000,
            storage_key="videos/test.mp4",
            duration_seconds=30.5,
        )
        assert record.id
        assert record.user_id == "user1"
        assert record.filename == "test.mp4"
        assert record.file_size == 1024000
        assert record.storage_key == "videos/test.mp4"
        assert record.duration_seconds == 30.5
        assert record.status == "pending"
        assert record.duplicate_rate is None
        assert record.duplicate_count == 0
        assert record.segments == []
        assert record.error_message == ""

    def test_create_strips_whitespace(self):
        record = DuplicationRecord.create(
            user_id="  user1  ",
            filename="  test.mp4  ",
            file_size=100,
            storage_key="key1",
        )
        assert record.user_id == "user1"
        assert record.filename == "test.mp4"

    def test_create_empty_user_id_raises(self):
        with pytest.raises(ValueError, match="user_id cannot be empty"):
            DuplicationRecord.create(
                user_id="",
                filename="test.mp4",
                file_size=100,
                storage_key="key1",
            )

    def test_create_empty_filename_raises(self):
        with pytest.raises(ValueError, match="filename cannot be empty"):
            DuplicationRecord.create(
                user_id="u1",
                filename="",
                file_size=100,
                storage_key="key1",
            )

    def test_create_zero_file_size_raises(self):
        with pytest.raises(ValueError, match="file_size must be positive"):
            DuplicationRecord.create(
                user_id="u1",
                filename="test.mp4",
                file_size=0,
                storage_key="key1",
            )

    def test_create_negative_file_size_raises(self):
        with pytest.raises(ValueError, match="file_size must be positive"):
            DuplicationRecord.create(
                user_id="u1",
                filename="test.mp4",
                file_size=-100,
                storage_key="key1",
            )


class TestDuplicationRecordStatus:
    def test_mark_processing(self):
        record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        record.mark_processing()
        assert record.status == "processing"

    def test_mark_completed(self):
        record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        seg = DuplicateSegment.create(
            source_start=0.0,
            source_end=5.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=5.0,
            similarity=90.0,
        )
        record.mark_completed(duplicate_rate=25.5, duplicate_count=3, segments=[seg])
        assert record.status == "completed"
        assert record.duplicate_rate == 25.5
        assert record.duplicate_count == 3
        assert len(record.segments) == 1

    def test_mark_completed_invalid_rate_raises(self):
        record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        with pytest.raises(ValueError, match="duplicate_rate must be between 0 and 100"):
            record.mark_completed(duplicate_rate=-1, duplicate_count=0, segments=[])
        with pytest.raises(ValueError, match="duplicate_rate must be between 0 and 100"):
            record.mark_completed(duplicate_rate=101, duplicate_count=0, segments=[])

    def test_mark_failed(self):
        record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        record.mark_failed("网络超时")
        assert record.status == "failed"
        assert record.error_message == "网络超时"

    def test_can_retry_only_failed(self):
        record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        assert record.can_retry() is False  # pending

        record.mark_processing()
        assert record.can_retry() is False  # processing

        record.mark_failed("error")
        assert record.can_retry() is True  # failed

        seg = DuplicateSegment.create(
            source_start=0.0,
            source_end=1.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=1.0,
            similarity=50.0,
        )
        record2 = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        record2.mark_completed(10.0, 1, [seg])
        assert record2.can_retry() is False  # completed

    def test_reset_for_retry(self):
        record = DuplicationRecord.create(user_id="u1", filename="t.mp4", file_size=100, storage_key="k1")
        seg = DuplicateSegment.create(
            source_start=0.0,
            source_end=1.0,
            matched_video_id="v1",
            matched_video_name="v",
            matched_start=0.0,
            matched_end=1.0,
            similarity=50.0,
        )
        record.mark_completed(50.0, 2, [seg])
        record.video_fingerprint = {"hash": "abc"}

        record.reset_for_retry()
        assert record.status == "pending"
        assert record.duplicate_rate is None
        assert record.duplicate_count == 0
        assert record.error_message == ""
        assert record.segments == []
        assert record.video_fingerprint is None
