# tests/unit/services/test_content_page_service.py
"""Unit tests for ContentPageService."""

import uuid

import pytest

from app.modules.cms.exceptions import (
    ContentPageNotFoundException,
    UnauthorizedContentPageAccessException,
)
from app.modules.cms.models import ContentPage
from app.modules.cms.services import ContentPageService, content_page_service


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceGetPageForVendor:
    """Test suite for ContentPageService.get_page_for_vendor()."""

    def test_get_platform_page_success(self, db, platform_about_page):
        """Test getting a platform default page."""
        result = content_page_service.get_page_for_vendor(
            db, slug="about", vendor_id=None
        )

        assert result is not None
        assert result.id == platform_about_page.id
        assert result.slug == "about"
        assert result.vendor_id is None

    def test_get_vendor_override_returns_vendor_page(
        self, db, platform_about_page, vendor_about_page, test_vendor
    ):
        """Test that vendor-specific override is returned over platform default."""
        result = content_page_service.get_page_for_vendor(
            db, slug="about", vendor_id=test_vendor.id
        )

        assert result is not None
        assert result.id == vendor_about_page.id
        assert result.vendor_id == test_vendor.id
        assert "Our Shop" in result.title

    def test_get_vendor_fallback_to_platform(
        self, db, platform_faq_page, test_vendor
    ):
        """Test fallback to platform default when vendor has no override."""
        result = content_page_service.get_page_for_vendor(
            db, slug="faq", vendor_id=test_vendor.id
        )

        assert result is not None
        assert result.id == platform_faq_page.id
        assert result.vendor_id is None

    def test_get_page_not_found(self, db):
        """Test getting non-existent page returns None."""
        result = content_page_service.get_page_for_vendor(
            db, slug="nonexistent", vendor_id=None
        )

        assert result is None

    def test_get_unpublished_page_excluded(self, db, platform_draft_page):
        """Test unpublished pages are excluded by default."""
        result = content_page_service.get_page_for_vendor(
            db, slug="draft-page", vendor_id=None
        )

        assert result is None

    def test_get_unpublished_page_included(self, db, platform_draft_page):
        """Test unpublished pages are included when requested."""
        result = content_page_service.get_page_for_vendor(
            db, slug="draft-page", vendor_id=None, include_unpublished=True
        )

        assert result is not None
        assert result.id == platform_draft_page.id
        assert result.is_published is False


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceListPagesForVendor:
    """Test suite for ContentPageService.list_pages_for_vendor()."""

    def test_list_platform_pages(self, db, all_platform_pages):
        """Test listing all platform pages."""
        result = content_page_service.list_pages_for_vendor(db, vendor_id=None)

        assert len(result) == 5
        slugs = {page.slug for page in result}
        assert "about" in slugs
        assert "faq" in slugs
        assert "privacy" in slugs
        assert "terms" in slugs
        assert "contact" in slugs

    def test_list_footer_pages(self, db, all_platform_pages):
        """Test listing pages marked for footer display."""
        result = content_page_service.list_pages_for_vendor(
            db, vendor_id=None, footer_only=True
        )

        # about, faq, contact have show_in_footer=True
        assert len(result) == 3
        for page in result:
            assert page.show_in_footer is True

    def test_list_header_pages(self, db, all_platform_pages):
        """Test listing pages marked for header display."""
        result = content_page_service.list_pages_for_vendor(
            db, vendor_id=None, header_only=True
        )

        # about, contact have show_in_header=True
        assert len(result) == 2
        for page in result:
            assert page.show_in_header is True

    def test_list_legal_pages(self, db, all_platform_pages):
        """Test listing pages marked for legal/bottom bar display."""
        result = content_page_service.list_pages_for_vendor(
            db, vendor_id=None, legal_only=True
        )

        # privacy, terms have show_in_legal=True
        assert len(result) == 2
        slugs = {page.slug for page in result}
        assert "privacy" in slugs
        assert "terms" in slugs
        for page in result:
            assert page.show_in_legal is True

    def test_list_vendor_pages_with_override(
        self, db, platform_about_page, platform_faq_page, vendor_about_page, test_vendor
    ):
        """Test vendor pages merge correctly with platform defaults."""
        result = content_page_service.list_pages_for_vendor(
            db, vendor_id=test_vendor.id
        )

        slugs = {page.slug: page for page in result}

        # Vendor override should be used for 'about'
        assert slugs["about"].vendor_id == test_vendor.id
        # Platform default should be used for 'faq'
        assert slugs["faq"].vendor_id is None

    def test_list_pages_sorted_by_display_order(self, db, content_page_factory):
        """Test pages are sorted by display_order."""
        # Create pages with specific display orders
        page1 = content_page_factory(db, slug="page-c", display_order=3)
        page2 = content_page_factory(db, slug="page-a", display_order=1)
        page3 = content_page_factory(db, slug="page-b", display_order=2)

        result = content_page_service.list_pages_for_vendor(db, vendor_id=None)

        # Should be sorted by display_order
        assert result[0].display_order <= result[1].display_order
        assert result[1].display_order <= result[2].display_order

    def test_list_excludes_unpublished(self, db, platform_about_page, platform_draft_page):
        """Test unpublished pages are excluded by default."""
        result = content_page_service.list_pages_for_vendor(db, vendor_id=None)

        slugs = {page.slug for page in result}
        assert "about" in slugs
        assert "draft-page" not in slugs

    def test_list_includes_unpublished(self, db, platform_about_page, platform_draft_page):
        """Test unpublished pages are included when requested."""
        result = content_page_service.list_pages_for_vendor(
            db, vendor_id=None, include_unpublished=True
        )

        slugs = {page.slug for page in result}
        assert "about" in slugs
        assert "draft-page" in slugs


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceCreatePage:
    """Test suite for ContentPageService.create_page()."""

    def test_create_platform_page(self, db, test_user):
        """Test creating a platform default page."""
        result = content_page_service.create_page(
            db,
            slug="new-page",
            title="New Page",
            content="<p>New content</p>",
            vendor_id=None,
            is_published=True,
            created_by=test_user.id,
        )

        assert result.id is not None
        assert result.slug == "new-page"
        assert result.title == "New Page"
        assert result.vendor_id is None
        assert result.is_published is True
        assert result.created_by == test_user.id

    def test_create_vendor_page(self, db, test_vendor, test_user):
        """Test creating a vendor-specific page."""
        result = content_page_service.create_page(
            db,
            slug="vendor-page",
            title="Vendor Page",
            content="<p>Vendor content</p>",
            vendor_id=test_vendor.id,
            is_published=True,
            created_by=test_user.id,
        )

        assert result.vendor_id == test_vendor.id
        assert result.slug == "vendor-page"

    def test_create_page_with_all_navigation_flags(self, db):
        """Test creating page with all navigation flags set."""
        result = content_page_service.create_page(
            db,
            slug="all-nav-page",
            title="All Navigation Page",
            content="<p>Appears everywhere</p>",
            show_in_header=True,
            show_in_footer=True,
            # Note: show_in_legal not in create_page params by default
        )

        assert result.show_in_header is True
        assert result.show_in_footer is True


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceUpdatePage:
    """Test suite for ContentPageService.update_page()."""

    def test_update_page_title(self, db, platform_about_page):
        """Test updating page title."""
        result = content_page_service.update_page(
            db, page_id=platform_about_page.id, title="Updated Title"
        )

        assert result.title == "Updated Title"

    def test_update_page_navigation_flags(self, db, platform_about_page):
        """Test updating navigation flags."""
        result = content_page_service.update_page(
            db,
            page_id=platform_about_page.id,
            show_in_header=False,
            show_in_footer=False,
        )

        assert result.show_in_header is False
        assert result.show_in_footer is False

    def test_update_page_not_found(self, db):
        """Test updating non-existent page returns None."""
        result = content_page_service.update_page(
            db, page_id=99999, title="New Title"
        )

        assert result is None

    def test_update_page_or_raise_not_found(self, db):
        """Test update_page_or_raise raises exception for non-existent page."""
        with pytest.raises(ContentPageNotFoundException) as exc_info:
            content_page_service.update_page_or_raise(
                db, page_id=99999, title="New Title"
            )

        assert exc_info.value.error_code == "CONTENT_PAGE_NOT_FOUND"


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceDeletePage:
    """Test suite for ContentPageService.delete_page()."""

    def test_delete_page_success(self, db, content_page_factory):
        """Test deleting a page."""
        page = content_page_factory(db, slug="to-delete")
        page_id = page.id

        result = content_page_service.delete_page(db, page_id=page_id)
        db.commit()  # Commit the deletion

        assert result is True

        # Verify page is deleted
        deleted = content_page_service.get_page_by_id(db, page_id)
        assert deleted is None

    def test_delete_page_not_found(self, db):
        """Test deleting non-existent page returns False."""
        result = content_page_service.delete_page(db, page_id=99999)

        assert result is False

    def test_delete_page_or_raise_not_found(self, db):
        """Test delete_page_or_raise raises exception for non-existent page."""
        with pytest.raises(ContentPageNotFoundException):
            content_page_service.delete_page_or_raise(db, page_id=99999)


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceVendorMethods:
    """Test suite for vendor-specific methods with ownership checks."""

    def test_update_vendor_page_success(self, db, vendor_about_page, test_vendor):
        """Test updating vendor page with correct ownership."""
        result = content_page_service.update_vendor_page(
            db,
            page_id=vendor_about_page.id,
            vendor_id=test_vendor.id,
            title="Updated Vendor Title",
        )

        assert result.title == "Updated Vendor Title"

    def test_update_vendor_page_wrong_vendor(
        self, db, vendor_about_page, other_company
    ):
        """Test updating vendor page with wrong vendor raises exception."""
        from app.modules.tenancy.models import Vendor

        # Create another vendor
        unique_id = str(uuid.uuid4())[:8]
        other_vendor = Vendor(
            company_id=other_company.id,
            vendor_code=f"OTHER_{unique_id.upper()}",
            subdomain=f"other{unique_id.lower()}",
            name=f"Other Vendor {unique_id}",
            is_active=True,
        )
        db.add(other_vendor)
        db.commit()
        db.refresh(other_vendor)

        with pytest.raises(UnauthorizedContentPageAccessException) as exc_info:
            content_page_service.update_vendor_page(
                db,
                page_id=vendor_about_page.id,
                vendor_id=other_vendor.id,
                title="Unauthorized Update",
            )

        assert exc_info.value.error_code == "CONTENT_PAGE_ACCESS_DENIED"

    def test_delete_vendor_page_success(
        self, db, vendor_shipping_page, test_vendor
    ):
        """Test deleting vendor page with correct ownership."""
        page_id = vendor_shipping_page.id

        content_page_service.delete_vendor_page(
            db, page_id=page_id, vendor_id=test_vendor.id
        )
        db.commit()  # Commit the deletion

        # Verify page is deleted
        deleted = content_page_service.get_page_by_id(db, page_id)
        assert deleted is None

    def test_delete_vendor_page_wrong_vendor(
        self, db, vendor_about_page, other_company
    ):
        """Test deleting vendor page with wrong vendor raises exception."""
        from app.modules.tenancy.models import Vendor

        # Create another vendor
        unique_id = str(uuid.uuid4())[:8]
        other_vendor = Vendor(
            company_id=other_company.id,
            vendor_code=f"OTHER2_{unique_id.upper()}",
            subdomain=f"other2{unique_id.lower()}",
            name=f"Other Vendor 2 {unique_id}",
            is_active=True,
        )
        db.add(other_vendor)
        db.commit()
        db.refresh(other_vendor)

        with pytest.raises(UnauthorizedContentPageAccessException):
            content_page_service.delete_vendor_page(
                db, page_id=vendor_about_page.id, vendor_id=other_vendor.id
            )


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageServiceListAllMethods:
    """Test suite for list_all methods."""

    def test_list_all_platform_pages(self, db, all_platform_pages):
        """Test listing only platform default pages."""
        result = content_page_service.list_all_platform_pages(db)

        assert len(result) == 5
        for page in result:
            assert page.vendor_id is None

    def test_list_all_vendor_pages(
        self, db, vendor_about_page, vendor_shipping_page, test_vendor
    ):
        """Test listing only vendor-specific pages."""
        result = content_page_service.list_all_vendor_pages(db, vendor_id=test_vendor.id)

        assert len(result) == 2
        for page in result:
            assert page.vendor_id == test_vendor.id

    def test_list_all_pages_filtered_by_vendor(
        self, db, platform_about_page, vendor_about_page, test_vendor
    ):
        """Test listing all pages filtered by vendor ID."""
        result = content_page_service.list_all_pages(db, vendor_id=test_vendor.id)

        # Should only include vendor pages, not platform defaults
        assert len(result) >= 1
        for page in result:
            assert page.vendor_id == test_vendor.id


@pytest.mark.unit
@pytest.mark.cms
class TestContentPageModel:
    """Test suite for ContentPage model methods."""

    def test_is_platform_default_property(self, db, platform_about_page):
        """Test is_platform_default property."""
        assert platform_about_page.is_platform_default is True
        assert platform_about_page.is_vendor_override is False

    def test_is_vendor_override_property(self, db, vendor_about_page):
        """Test is_vendor_override property."""
        assert vendor_about_page.is_platform_default is False
        assert vendor_about_page.is_vendor_override is True

    def test_to_dict_includes_navigation_flags(self, db, platform_privacy_page):
        """Test to_dict includes all navigation flags."""
        result = platform_privacy_page.to_dict()

        assert "show_in_footer" in result
        assert "show_in_header" in result
        assert "show_in_legal" in result
        assert result["show_in_legal"] is True

    def test_to_dict_includes_all_fields(self, db, platform_about_page):
        """Test to_dict includes all expected fields."""
        result = platform_about_page.to_dict()

        expected_fields = [
            "id",
            "vendor_id",
            "slug",
            "title",
            "content",
            "content_format",
            "template",
            "meta_description",
            "meta_keywords",
            "is_published",
            "published_at",
            "display_order",
            "show_in_footer",
            "show_in_header",
            "show_in_legal",
            "is_platform_default",
            "is_vendor_override",
            "created_at",
            "updated_at",
            "created_by",
            "updated_by",
        ]

        for field in expected_fields:
            assert field in result, f"Missing field: {field}"
