# #region Test.Maintenance.BannerRenderer.Coverage [C:3] [TYPE Module] [SEMANTICS test, maintenance, banner, coverage, REMOVED]
# @BRIEF Tests for banner renderer uncovered paths — rebuild_banner with no text but chart_id present, update exception.
# @RELATION BINDS_TO -> [MaintenanceBannerRenderer]

import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from src.models.maintenance import (
    MaintenanceDashboardBanner,
    MaintenanceDashboardBannerStatus,
    MaintenanceDashboardState,
    MaintenanceEvent,
    MaintenanceEventStatus,
    MaintenanceSettings,
)


class TestRebuildBannerCoverage:
    """Cover the 'no active events → mark REMOVED' path with chart_id set."""

    @pytest.mark.asyncio
    async def test_no_active_events_with_chart_id_marks_removed(self):
        """banner_text empty but chart_id set → status set to REMOVED, returns True."""
        from src.services.maintenance._banner_renderer import rebuild_banner

        db = MagicMock()
        banner = MaintenanceDashboardBanner(
            id="b1", status=MaintenanceDashboardBannerStatus.ACTIVE,
            chart_id=456, dashboard_id=1, environment_id="e1", banner_text=""
        )
        settings = MaintenanceSettings(id="default", banner_template="Tpl", display_timezone="UTC")

        # Build per-model query mock objects
        banner_q = MagicMock()
        banner_q.filter.return_value.first.return_value = banner

        settings_q = MagicMock()
        settings_q.filter.return_value.first.return_value = settings

        state_q = MagicMock()
        state_q.filter.return_value.all.return_value = []

        def query_side(model):
            if model == MaintenanceDashboardBanner:
                return banner_q
            if model == MaintenanceSettings:
                return settings_q
            if model == MaintenanceDashboardState:
                return state_q
            return MagicMock()
        db.query.side_effect = query_side

        result = await rebuild_banner("b1", db, AsyncMock())
        assert result is True
        assert banner.status == MaintenanceDashboardBannerStatus.REMOVED

    @pytest.mark.asyncio
    async def test_update_banner_exception(self):
        """update_banner_on_dashboard raises → returns False."""
        from src.services.maintenance._banner_renderer import rebuild_banner

        db = MagicMock()
        banner = MaintenanceDashboardBanner(
            id="b1", status=MaintenanceDashboardBannerStatus.ACTIVE,
            chart_id=123, dashboard_id=1, environment_id="e1", banner_text=""
        )
        settings = MaintenanceSettings(id="default", banner_template="Msg: {message}", display_timezone="UTC")
        state = MaintenanceDashboardState(event_id="evt1", dashboard_id=1, banner_id="b1")
        ev = MaintenanceEvent(id="evt1", message="Hello", status=MaintenanceEventStatus.ACTIVE)

        # _build_banner_text_for_dashboard does:
        #   .filter(banner_id == x, status == y).all()
        #   (single .filter() call with TWO args, not two chained calls)
        state_q = MagicMock()
        state_q.filter.return_value.all.return_value = [state]

        # Build other query mocks
        def query_side(model):
            if model == MaintenanceDashboardBanner:
                m = MagicMock()
                m.filter.return_value.first.return_value = banner
                return m
            if model == MaintenanceSettings:
                m = MagicMock()
                m.filter.return_value.first.return_value = settings
                return m
            if model == MaintenanceDashboardState:
                return state_q
            if model == MaintenanceEvent:
                m = MagicMock()
                m.filter.return_value.first.return_value = ev
                return m
            return MagicMock()
        db.query.side_effect = query_side

        mock_superset = AsyncMock()
        mock_superset.update_banner_on_dashboard.side_effect = Exception("Update failed")

        result = await rebuild_banner("b1", db, mock_superset)
        assert result is False
# #endregion Test.Maintenance.BannerRenderer.Coverage


# #region Test.BannerRenderer.DeadCode [C:1] [TYPE Module] [SEMANTICS test, coverage, dead-code]
# @BRIEF Documents dead code in build_banner_text: inner `if len(events) == 1` inside the
#   multi-event else block (lines 96-99) is always False. This is defensive/dead code.
class TestBannerTextDeadCode:
    """Documents dead code in build_banner_text multi-event path (lines 96-102).

    The `if len(events) == 1:` check at line 96 is inside the `else` block at line 81,
    which only executes when `len(events) > 1` (from the outer `if len(events) == 1:` at line 71).
    Therefore lines 97-99 (inner substitution of start_time/end_time) are unreachable.
    The `else` branch at lines 100-102 correctly replaces both with empty string for multi-event.

    This is defensive coding — the inner condition can never be True.
    """

    @staticmethod
    def test_multi_event_never_enters_single_event_substitution():
        """Verify the outer multi-event path is the one being used."""
        from src.services.maintenance._banner_renderer import build_banner_text

        events = [
            {"start_time": "2024-01-01T00:00:00Z", "end_time": "2024-01-02T00:00:00Z", "message": "Event 1"},
            {"start_time": "2024-01-03T00:00:00Z", "end_time": "2024-01-04T00:00:00Z", "message": "Event 2"},
        ]
        template = "Start: {start_time} End: {end_time} Msg: {message}"
        result = build_banner_text(events, template)
        # Multi-event path replaces all {start_time} and {end_time} with empty string
        assert "{start_time}" not in result or result.startswith("Start: ")
        assert "{end_time}" not in result or result.startswith("Start: ")
# #endregion Test.BannerRenderer.DeadCode
