"""Unit tests for theme/layout policies — Phase 11.3 (versioned)."""
from __future__ import annotations

import pytest

from app.models.types import (
    LayoutStatus,
    LayoutVersionStatus,
    ThemeStatus,
    ThemeVersionStatus,
)
from app.policies.themes import (
    LayoutLifecyclePolicy,
    LayoutVersionPolicy,
    PageLayoutBindingPolicy,
    SiteThemeBindingPolicy,
    ThemeLifecyclePolicy,
    ThemeVersionPolicy,
)
from shared.exceptions import AppError


def test_theme_lifecycle_transitions():
    policy = ThemeLifecyclePolicy()
    assert (
        policy.assert_status_change(
            current=ThemeStatus.DRAFT, target=ThemeStatus.ACTIVE
        )
        == ThemeStatus.ACTIVE
    )
    with pytest.raises(AppError):
        policy.assert_status_change(
            current=ThemeStatus.ARCHIVED, target=ThemeStatus.ACTIVE
        )


def test_layout_lifecycle_transitions():
    policy = LayoutLifecyclePolicy()
    assert (
        policy.assert_status_change(
            current=LayoutStatus.ACTIVE, target=LayoutStatus.DEPRECATED
        )
        == LayoutStatus.DEPRECATED
    )
    with pytest.raises(AppError):
        policy.assert_status_change(
            current=LayoutStatus.ARCHIVED, target=LayoutStatus.ACTIVE
        )


class _ThemeVersion:
    def __init__(self, status, is_deleted=False):
        self.status = status
        self.is_deleted = is_deleted


class _LayoutVersion:
    def __init__(self, status, is_deleted=False):
        self.status = status
        self.is_deleted = is_deleted


def test_theme_version_policy_update_publish_retire():
    policy = ThemeVersionPolicy()

    policy.assert_can_update(_ThemeVersion(ThemeVersionStatus.DRAFT))
    with pytest.raises(AppError) as exc:
        policy.assert_can_update(_ThemeVersion(ThemeVersionStatus.PUBLISHED))
    assert exc.value.error_code == "theme_version_not_mutable"

    policy.assert_publish(current=ThemeVersionStatus.DRAFT)
    with pytest.raises(AppError):
        policy.assert_publish(current=ThemeVersionStatus.RETIRED)

    policy.assert_retire(current=ThemeVersionStatus.PUBLISHED)
    with pytest.raises(AppError):
        policy.assert_retire(current=ThemeVersionStatus.DRAFT)


def test_layout_version_policy_update_publish_retire():
    policy = LayoutVersionPolicy()

    policy.assert_can_update(_LayoutVersion(LayoutVersionStatus.DRAFT))
    with pytest.raises(AppError) as exc:
        policy.assert_can_update(_LayoutVersion(LayoutVersionStatus.RETIRED))
    assert exc.value.error_code == "layout_version_not_mutable"

    policy.assert_publish(current=LayoutVersionStatus.DRAFT)
    with pytest.raises(AppError):
        policy.assert_publish(current=LayoutVersionStatus.RETIRED)

    policy.assert_retire(current=LayoutVersionStatus.PUBLISHED)
    with pytest.raises(AppError):
        policy.assert_retire(current=LayoutVersionStatus.DRAFT)


def test_site_theme_binding_policy_assignable():
    policy = SiteThemeBindingPolicy()

    class _Theme:
        status = ThemeStatus.DRAFT
        is_deleted = False

    with pytest.raises(AppError) as exc:
        policy.assert_theme_assignable(_Theme())
    assert exc.value.error_code == "theme_not_active"

    class _ActiveTheme:
        status = ThemeStatus.ACTIVE
        is_deleted = False

    policy.assert_theme_assignable(_ActiveTheme())

    with pytest.raises(AppError) as exc:
        policy.assert_version_assignable(_ThemeVersion(ThemeVersionStatus.DRAFT))
    assert exc.value.error_code == "theme_version_not_published"

    policy.assert_version_assignable(_ThemeVersion(ThemeVersionStatus.PUBLISHED))


def test_page_layout_binding_policy_assignable():
    policy = PageLayoutBindingPolicy()

    class _Layout:
        status = LayoutStatus.DRAFT
        is_deleted = False

    with pytest.raises(AppError) as exc:
        policy.assert_layout_assignable(_Layout())
    assert exc.value.error_code == "layout_not_active"

    class _ActiveLayout:
        status = LayoutStatus.ACTIVE
        is_deleted = False

    policy.assert_layout_assignable(_ActiveLayout())

    with pytest.raises(AppError) as exc:
        policy.assert_version_assignable(_LayoutVersion(LayoutVersionStatus.DRAFT))
    assert exc.value.error_code == "layout_version_not_published"

    policy.assert_version_assignable(_LayoutVersion(LayoutVersionStatus.PUBLISHED))
