"""
Demo Session Repository
Data access layer for demo sessions
"""

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update
from datetime import datetime, timezone
from typing import Optional, List, Dict, Any
from uuid import UUID
import structlog

from app.models import DemoSession, DemoSessionStatus

logger = structlog.get_logger()


class DemoSessionRepository:
    """Repository for DemoSession data access"""

    def __init__(self, db: AsyncSession):
        self.db = db

    async def create(self, session_data: Dict[str, Any]) -> DemoSession:
        """
        Create a new demo session

        Args:
            session_data: Dictionary with session attributes

        Returns:
            Created DemoSession instance
        """
        session = DemoSession(**session_data)
        self.db.add(session)
        await self.db.commit()
        await self.db.refresh(session)
        return session

    async def get_by_session_id(self, session_id: str) -> Optional[DemoSession]:
        """
        Get session by session_id

        Args:
            session_id: Session ID string

        Returns:
            DemoSession or None if not found
        """
        result = await self.db.execute(
            select(DemoSession).where(DemoSession.session_id == session_id)
        )
        return result.scalar_one_or_none()

    async def get_by_virtual_tenant_id(self, virtual_tenant_id: UUID) -> Optional[DemoSession]:
        """
        Get session by virtual tenant ID

        Args:
            virtual_tenant_id: Virtual tenant UUID

        Returns:
            DemoSession or None if not found
        """
        result = await self.db.execute(
            select(DemoSession).where(DemoSession.virtual_tenant_id == virtual_tenant_id)
        )
        return result.scalar_one_or_none()

    async def update(self, session: DemoSession) -> DemoSession:
        """
        Update an existing session

        Args:
            session: DemoSession instance with updates

        Returns:
            Updated DemoSession instance
        """
        await self.db.commit()
        await self.db.refresh(session)
        return session

    async def update_fields(self, session_id: str, **fields) -> None:
        """
        Update specific fields of a session

        Args:
            session_id: Session ID to update
            **fields: Field names and values to update
        """
        await self.db.execute(
            update(DemoSession)
            .where(DemoSession.session_id == session_id)
            .values(**fields)
        )
        await self.db.commit()

    async def update_activity(self, session_id: str) -> None:
        """
        Update last activity timestamp and increment request count

        Args:
            session_id: Session ID to update
        """
        await self.db.execute(
            update(DemoSession)
            .where(DemoSession.session_id == session_id)
            .values(
                last_activity_at=datetime.now(timezone.utc),
                request_count=DemoSession.request_count + 1
            )
        )
        await self.db.commit()

    async def mark_data_cloned(self, session_id: str) -> None:
        """
        Mark session as having data cloned

        Args:
            session_id: Session ID to update
        """
        await self.update_fields(session_id, data_cloned=True)

    async def mark_redis_populated(self, session_id: str) -> None:
        """
        Mark session as having Redis data populated

        Args:
            session_id: Session ID to update
        """
        await self.update_fields(session_id, redis_populated=True)

    async def destroy(self, session_id: str) -> None:
        """
        Mark session as destroyed

        Args:
            session_id: Session ID to destroy
        """
        await self.update_fields(
            session_id,
            status=DemoSessionStatus.DESTROYED,
            destroyed_at=datetime.now(timezone.utc)
        )

    async def get_active_sessions_count(self) -> int:
        """
        Get count of active sessions

        Returns:
            Number of active sessions
        """
        result = await self.db.execute(
            select(DemoSession).where(DemoSession.status == DemoSessionStatus.ACTIVE)
        )
        return len(result.scalars().all())

    async def get_all_sessions(self) -> List[DemoSession]:
        """
        Get all demo sessions

        Returns:
            List of all DemoSession instances
        """
        result = await self.db.execute(select(DemoSession))
        return result.scalars().all()

    async def get_sessions_by_status(self, status: DemoSessionStatus) -> List[DemoSession]:
        """
        Get sessions by status

        Args:
            status: DemoSessionStatus to filter by

        Returns:
            List of DemoSession instances with the specified status
        """
        result = await self.db.execute(
            select(DemoSession).where(DemoSession.status == status)
        )
        return result.scalars().all()

    async def get_session_stats(self) -> Dict[str, Any]:
        """
        Get session statistics

        Returns:
            Dictionary with session statistics
        """
        all_sessions = await self.get_all_sessions()
        active_sessions = [s for s in all_sessions if s.status == DemoSessionStatus.ACTIVE]

        return {
            "total_sessions": len(all_sessions),
            "active_sessions": len(active_sessions),
            "expired_sessions": len([s for s in all_sessions if s.status == DemoSessionStatus.EXPIRED]),
            "destroyed_sessions": len([s for s in all_sessions if s.status == DemoSessionStatus.DESTROYED]),
            "avg_duration_minutes": sum(
                (s.destroyed_at - s.created_at).total_seconds() / 60
                for s in all_sessions if s.destroyed_at
            ) / max(len([s for s in all_sessions if s.destroyed_at]), 1),
            "total_requests": sum(s.request_count for s in all_sessions)
        }
