"""GeneratedVideo + VerificationCode 领域模型测试."""

from __future__ import annotations

from datetime import timedelta

import pytest

from packages.domain.generated_video import GeneratedVideo
from packages.domain.verification_code import VerificationCode


class TestGeneratedVideo:
    """GeneratedVideo 生成视频实体测试."""

    def test_create_success(self):
        """创建成功."""
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="task_1",
            name="我的视频.mp4",
            file_url="https://example.com/out.mp4",
            user_id="u1",
            file_size=1024000,
            duration=30.5,
            width=1080,
            height=1920,
            fps=25.0,
        )
        assert video.id is not None
        assert len(video.id) == 32
        assert video.project_id == "p1"
        assert video.generation_task_id == "task_1"
        assert video.name == "我的视频.mp4"
        assert video.file_url == "https://example.com/out.mp4"
        assert video.user_id == "u1"
        assert video.file_size == 1024000
        assert video.duration == pytest.approx(30.5)
        assert video.width == 1080
        assert video.height == 1920
        assert video.fps == pytest.approx(25.0)
        assert video.status == "completed"
        assert video.review_status == "pending_review"
        assert video.is_duplicate is False
        assert video.duplicate_of is None
        assert video.generation_params == {}

    def test_create_empty_project_id_raises(self):
        """空project_id抛异常."""
        with pytest.raises(ValueError, match="project_id"):
            GeneratedVideo.create(
                project_id="  ",
                generation_task_id="t1",
                name="v.mp4",
                file_url="https://x.com/v.mp4",
            )

    def test_create_empty_task_id_raises(self):
        """空generation_task_id抛异常."""
        with pytest.raises(ValueError, match="generation_task_id"):
            GeneratedVideo.create(
                project_id="p1",
                generation_task_id="",
                name="v.mp4",
                file_url="https://x.com/v.mp4",
            )

    def test_create_empty_name_raises(self):
        """空name抛异常."""
        with pytest.raises(ValueError, match="name"):
            GeneratedVideo.create(
                project_id="p1",
                generation_task_id="t1",
                name="   ",
                file_url="https://x.com/v.mp4",
            )

    def test_create_empty_file_url_raises(self):
        """空file_url抛异常."""
        with pytest.raises(ValueError, match="file_url"):
            GeneratedVideo.create(
                project_id="p1",
                generation_task_id="t1",
                name="v.mp4",
                file_url="",
            )

    def test_create_strips_whitespace(self):
        """首尾空白被去除."""
        video = GeneratedVideo.create(
            project_id="  p1  ",
            generation_task_id="  t1  ",
            name="  视频.mp4  ",
            file_url="  https://x.com/v.mp4  ",
        )
        assert video.project_id == "p1"
        assert video.generation_task_id == "t1"
        assert video.name == "视频.mp4"
        assert video.file_url == "https://x.com/v.mp4"

    def test_default_values(self):
        """默认值正确."""
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="t1",
            name="v.mp4",
            file_url="https://x.com/v.mp4",
        )
        assert video.user_id == ""
        assert video.file_size == 0
        assert video.duration == 0.0
        assert video.width == 0
        assert video.height == 0
        assert video.fps == 0.0
        assert video.thumbnail_url is None
        assert video.generation_params == {}

    def test_generation_params_none_becomes_empty(self):
        """generation_params=None → {}."""
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="t1",
            name="v.mp4",
            file_url="https://x.com/v.mp4",
            generation_params=None,
        )
        assert video.generation_params == {}

    def test_custom_generation_params(self):
        """自定义生成参数."""
        params = {"mode": "smart", "resolution": "1080x1920"}
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="t1",
            name="v.mp4",
            file_url="https://x.com/v.mp4",
            generation_params=params,
        )
        assert video.generation_params == params

    def test_duplicate_flag(self):
        """重复标记可以设置."""
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="t1",
            name="v.mp4",
            file_url="https://x.com/v.mp4",
        )
        video.is_duplicate = True
        video.duplicate_of = "other_video_id"
        assert video.is_duplicate is True
        assert video.duplicate_of == "other_video_id"

    def test_custom_status(self):
        """自定义状态."""
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="t1",
            name="v.mp4",
            file_url="https://x.com/v.mp4",
        )
        video.status = "failed"
        assert video.status == "failed"

    def test_thumbnail_url(self):
        """缩略图URL."""
        video = GeneratedVideo.create(
            project_id="p1",
            generation_task_id="t1",
            name="v.mp4",
            file_url="https://x.com/v.mp4",
            thumbnail_url="https://x.com/thumb.jpg",
        )
        assert video.thumbnail_url == "https://x.com/thumb.jpg"


class TestVerificationCodeCreate:
    """VerificationCode 创建测试."""

    def test_create_success(self):
        """创建验证码成功."""
        vc = VerificationCode.create(
            recipient="test@example.com",
            code_type="email_bind",
            ttl_seconds=300,
        )
        assert vc.id is not None
        assert len(vc.id) == 32
        assert vc.recipient == "test@example.com"
        assert vc.code_type == "email_bind"
        assert len(vc.code) == 6  # 默认6位数字
        assert vc.code.isdigit()  # 纯数字
        assert vc.used_at is None
        assert vc.attempts == 0

    def test_create_with_custom_code(self):
        """自定义验证码."""
        vc = VerificationCode.create(
            recipient="u@test.com",
            code_type="email_login",
            custom_code="123456",
        )
        assert vc.code == "123456"

    def test_create_recipient_stripped(self):
        """收件人空白被去除."""
        vc = VerificationCode.create(
            recipient="  test@example.com  ",
            code_type="email_bind",
        )
        assert vc.recipient == "test@example.com"

    def test_expiry_time_correct(self):
        """过期时间正确（5分钟后）."""
        from datetime import datetime, timezone

        before = datetime.now(timezone.utc) + timedelta(seconds=299)
        vc = VerificationCode.create(
            recipient="u@test.com",
            code_type="reset_password",
            ttl_seconds=300,
        )
        after = datetime.now(timezone.utc) + timedelta(seconds=301)
        assert before <= vc.expires_at <= after

    def test_custom_ttl(self):
        """自定义过期时间."""
        vc = VerificationCode.create(
            recipient="u@test.com",
            code_type="phone_login",
            ttl_seconds=60,
        )
        from datetime import datetime, timezone

        now = datetime.now(timezone.utc)
        # 应该在1分钟左右过期
        diff = (vc.expires_at - now).total_seconds()
        assert 0 < diff < 70


class TestVerificationCodeProperties:
    """VerificationCode 属性方法测试."""

    def test_is_expired_false_for_new(self):
        """新创建的验证码未过期."""
        vc = VerificationCode.create(
            recipient="u@test.com",
            code_type="email_bind",
            ttl_seconds=300,
        )
        assert vc.is_expired is False

    def test_is_expired_true_when_past(self):
        """已过期的验证码is_expired=True."""
        vc = VerificationCode.create(
            recipient="u@test.com",
            code_type="email_bind",
            ttl_seconds=1,
        )
        # 手动改过期时间到过去
        from datetime import datetime, timedelta, timezone

        vc.expires_at = datetime.now(timezone.utc) - timedelta(seconds=1)
        assert vc.is_expired is True

    def test_is_used_false_by_default(self):
        """默认未使用."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind")
        assert vc.is_used is False

    def test_is_valid_fresh_code(self):
        """新验证码有效."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind")
        assert vc.is_valid is True

    def test_is_valid_expired(self):
        """过期的验证码无效."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind", ttl_seconds=1)
        from datetime import datetime, timedelta, timezone

        vc.expires_at = datetime.now(timezone.utc) - timedelta(seconds=1)
        assert vc.is_valid is False

    def test_is_valid_used(self):
        """已使用的验证码无效."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind")
        vc.mark_used()
        assert vc.is_valid is False


class TestVerificationCodeActions:
    """VerificationCode 操作方法测试."""

    def test_mark_used(self):
        """标记使用."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind")
        vc.mark_used()
        assert vc.is_used is True
        assert vc.used_at is not None

    def test_mark_used_twice(self):
        """标记两次也没问题."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind")
        vc.mark_used()
        first_time = vc.used_at
        vc.mark_used()
        # 第二次会覆盖时间
        assert vc.used_at >= first_time

    def test_increment_attempts(self):
        """增加尝试次数."""
        vc = VerificationCode.create(recipient="u@test.com", code_type="email_bind")
        assert vc.attempts == 0
        vc.increment_attempts()
        assert vc.attempts == 1
        vc.increment_attempts()
        assert vc.attempts == 2

    def test_all_code_types_supported(self):
        """支持所有code_type."""
        for code_type in ["email_bind", "phone_bind", "email_login", "phone_login", "reset_password"]:
            vc = VerificationCode.create(recipient="u@test.com", code_type=code_type)
            assert vc.code_type == code_type
