"""
StatViewRefresher - Refresh materialized views for statistics.

Provides methods to refresh PostgreSQL materialized views created in migration 004.
Uses REFRESH MATERIALIZED VIEW CONCURRENTLY to allow reads during refresh.

Author: Claude
Date: 2025-11-07
"""

import logging

from sqlalchemy import text

from app.database.session import AsyncSessionLocal

logger = logging.getLogger(f"{__name__}.StatViewRefresher")


class StatViewRefresher:
    """
    Service for refreshing materialized views containing game statistics.

    Refreshes three materialized views:
    - batting_game_stats: Player batting statistics aggregated from plays
    - pitching_game_stats: Player pitching statistics aggregated from plays
    - game_stats: Team totals and linescore aggregated from plays

    Uses CONCURRENTLY option to allow reads during refresh (requires unique indexes).
    """

    # View names in order (dependencies matter for concurrent refresh)
    VIEWS = ["batting_game_stats", "pitching_game_stats", "game_stats"]

    async def refresh_all(self) -> None:
        """
        Refresh all stat materialized views concurrently.

        Refreshes views in order to handle any dependencies.
        Uses REFRESH MATERIALIZED VIEW CONCURRENTLY to avoid locking.

        Raises:
            Exception: If any refresh fails
        """
        async with AsyncSessionLocal() as session:
            try:
                for view_name in self.VIEWS:
                    logger.debug(f"Refreshing materialized view: {view_name}")
                    await self._refresh_view(session, view_name)

                await session.commit()
                logger.info(
                    f"Successfully refreshed all {len(self.VIEWS)} materialized views"
                )

            except Exception as e:
                await session.rollback()
                logger.error(
                    f"Failed to refresh materialized views: {e}", exc_info=True
                )
                raise

    async def refresh_view(self, view_name: str) -> None:
        """
        Refresh a single materialized view.

        Args:
            view_name: Name of the materialized view to refresh

        Raises:
            ValueError: If view_name is not recognized
            Exception: If refresh fails
        """
        if view_name not in self.VIEWS:
            raise ValueError(f"Unknown view: {view_name}. Must be one of {self.VIEWS}")

        async with AsyncSessionLocal() as session:
            try:
                await self._refresh_view(session, view_name)
                await session.commit()
                logger.info(f"Successfully refreshed materialized view: {view_name}")

            except Exception as e:
                await session.rollback()
                logger.error(f"Failed to refresh view {view_name}: {e}", exc_info=True)
                raise

    async def _refresh_view(self, session, view_name: str) -> None:
        """
        Execute REFRESH MATERIALIZED VIEW CONCURRENTLY statement.

        Args:
            session: Active database session
            view_name: Name of the view to refresh
        """
        # CONCURRENTLY requires unique index (created in migration)
        # Allows reads during refresh but is slightly slower
        query = text(f"REFRESH MATERIALIZED VIEW CONCURRENTLY {view_name}")
        await session.execute(query)
        logger.debug(f"Executed: REFRESH MATERIALIZED VIEW CONCURRENTLY {view_name}")


# Singleton instance
stat_view_refresher = StatViewRefresher()
