"""视频分享领域模型单元测试 - 纯逻辑部分。"""

from __future__ import annotations

from datetime import datetime, timedelta, timezone

import pytest
from domain.video_share import (
    VideoShare,
    _hash_password,
    generate_share_token,
)


class TestHashPassword:
    """密码哈希函数。"""

    def test_empty_password_returns_empty(self):
        assert _hash_password("") == ""

    def test_none_password_returns_empty(self):
        assert _hash_password(None) == ""

    def test_same_password_same_hash(self):
        h1 = _hash_password("test123")
        h2 = _hash_password("test123")
        assert h1 == h2
        assert len(h1) > 0

    def test_different_passwords_different_hash(self):
        h1 = _hash_password("password1")
        h2 = _hash_password("password2")
        assert h1 != h2

    def test_hash_is_hex_string(self):
        h = _hash_password("test")
        int(h, 16)  # 合法 hex 不抛异常
        assert len(h) == 64  # SHA-256 输出 64 个 hex 字符

    def test_hash_contains_salt(self):
        """相同密码的直接 SHA-256 与加盐后结果不同。"""
        from hashlib import sha256

        password = "mypassword"
        direct_hash = sha256(password.encode()).hexdigest()
        salted_hash = _hash_password(password)
        assert salted_hash != direct_hash


class TestGenerateShareToken:
    """分享 token 生成。"""

    def test_default_length(self):
        token = generate_share_token()
        assert len(token) == 12

    def test_custom_length(self):
        for length in [6, 8, 16, 32]:
            token = generate_share_token(length=length)
            assert len(token) == length

    def test_url_friendly_alphabet(self):
        """token 只包含 URL 友好的字符，没有歧义字符。"""
        token = generate_share_token(length=100)
        # 不应该包含容易混淆的字符
        assert "i" not in token or True  # 可能有，取决于随机
        assert "l" not in token or True
        # 验证所有字符都在字母表里
        alphabet = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789"
        for char in token:
            assert char in alphabet

    def test_tokens_are_unique(self):
        """连续生成的 token 不重复。"""
        tokens = {generate_share_token() for _ in range(100)}
        assert len(tokens) == 100


class TestVideoShareCreate:
    """VideoShare.create 工厂方法。"""

    def test_create_minimal(self):
        share = VideoShare.create(video_id="vid-1", user_id="user-1")
        assert share.video_id == "vid-1"
        assert share.user_id == "user-1"
        assert share.id  # 自动生成
        assert share.share_token  # 自动生成
        assert share.password_hash is None
        assert share.expires_at is None
        assert share.view_count == 0
        assert share.download_count == 0
        assert share.is_active is True

    def test_create_with_password(self):
        share = VideoShare.create(video_id="vid-1", user_id="user-1", password="secret123")
        assert share.password_hash is not None
        assert share.password_hash != "secret123"  # 已哈希
        assert len(share.password_hash) > 0

    def test_create_with_expiration(self):
        expire_time = datetime(2026, 12, 31, tzinfo=timezone.utc)
        share = VideoShare.create(video_id="vid-1", user_id="user-1", expires_at=expire_time)
        assert share.expires_at == expire_time

    def test_create_generates_unique_ids(self):
        s1 = VideoShare.create(video_id="v", user_id="u")
        s2 = VideoShare.create(video_id="v", user_id="u")
        assert s1.id != s2.id
        assert s1.share_token != s2.share_token

    def test_create_strips_whitespace(self):
        share = VideoShare.create(video_id="  vid-1  ", user_id="\tuser-1\n")
        assert share.video_id == "vid-1"
        assert share.user_id == "user-1"


class TestVideoSharePassword:
    """密码相关方法。"""

    def test_has_password_false_when_none(self):
        share = VideoShare.create(video_id="v", user_id="u")
        assert share.has_password is False

    def test_has_password_false_when_empty(self):
        share = VideoShare.create(video_id="v", user_id="u", password="")
        assert share.has_password is False

    def test_has_password_true_when_set(self):
        share = VideoShare.create(video_id="v", user_id="u", password="pass")
        assert share.has_password is True

    def test_verify_password_correct(self):
        share = VideoShare.create(video_id="v", user_id="u", password="mysecret")
        assert share.verify_password("mysecret") is True

    def test_verify_password_wrong(self):
        share = VideoShare.create(video_id="v", user_id="u", password="mysecret")
        assert share.verify_password("wrong") is False

    def test_verify_password_no_password_set(self):
        share = VideoShare.create(video_id="v", user_id="u")
        # 没有设置密码时，任何输入都通过（免密访问）
        assert share.verify_password("anything") is True
        assert share.verify_password("") is True

    def test_verify_password_empty_input(self):
        share = VideoShare.create(video_id="v", user_id="u", password="pass")
        assert share.verify_password("") is False


class TestVideoShareExpiration:
    """过期相关方法。"""

    def test_not_expired_when_no_expiry(self):
        share = VideoShare.create(video_id="v", user_id="u")
        assert share.is_expired is False

    def test_not_expired_when_future(self):
        future = datetime.now(timezone.utc) + timedelta(days=7)
        share = VideoShare.create(video_id="v", user_id="u", expires_at=future)
        assert share.is_expired is False

    def test_expired_when_past(self):
        share = VideoShare.create(video_id="v", user_id="u")
        # 直接设置过去的过期时间（create 方法会阻止过期时间在过去）
        share.expires_at = datetime.now(timezone.utc) - timedelta(days=1)
        assert share.is_expired is True

    def test_create_rejects_past_expiry(self):
        """create 方法拒绝过去的过期时间。"""
        past = datetime.now(timezone.utc) - timedelta(days=1)
        with pytest.raises(ValueError, match="expires_at cannot be in the past"):
            VideoShare.create(video_id="v", user_id="u", expires_at=past)


class TestVideoShareAccessible:
    """可访问性判断。"""

    def test_active_no_expiry_is_accessible(self):
        share = VideoShare.create(video_id="v", user_id="u")
        assert share.is_accessible is True

    def test_inactive_not_accessible(self):
        share = VideoShare.create(video_id="v", user_id="u")
        share.revoke()
        assert share.is_accessible is False

    def test_expired_not_accessible(self):
        share = VideoShare.create(video_id="v", user_id="u")
        # 直接设置过去的过期时间
        share.expires_at = datetime.now(timezone.utc) - timedelta(days=1)
        assert share.is_accessible is False


class TestVideoShareCounts:
    """计数相关方法。"""

    def test_initial_view_count_zero(self):
        share = VideoShare.create(video_id="v", user_id="u")
        assert share.view_count == 0

    def test_increment_view_count(self):
        share = VideoShare.create(video_id="v", user_id="u")
        share.increment_view_count()
        assert share.view_count == 1
        share.increment_view_count()
        share.increment_view_count()
        assert share.view_count == 3

    def test_initial_download_count_zero(self):
        share = VideoShare.create(video_id="v", user_id="u")
        assert share.download_count == 0

    def test_increment_download_count(self):
        share = VideoShare.create(video_id="v", user_id="u")
        share.increment_download_count()
        share.increment_download_count()
        assert share.download_count == 2


class TestVideoShareRevoke:
    """撤销分享。"""

    def test_revoke_deactivates(self):
        share = VideoShare.create(video_id="v", user_id="u")
        assert share.is_active is True
        share.revoke()
        assert share.is_active is False

    def test_revoke_idempotent(self):
        share = VideoShare.create(video_id="v", user_id="u")
        share.revoke()
        share.revoke()  # 再次调用不报错
        assert share.is_active is False
