"""
Cross-switch service for managing camera-to-monitor routing
"""
from typing import List, Optional, Dict, Any
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, desc
import uuid
import structlog

from models.crossswitch_route import CrossSwitchRoute
from models.audit_log import AuditLog
from clients.sdk_bridge_client import sdk_bridge_client
from clients.redis_client import redis_client

logger = structlog.get_logger()


class CrossSwitchService:
    """Service for cross-switching operations"""

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

    async def execute_crossswitch(
        self,
        camera_id: int,
        monitor_id: int,
        user_id: uuid.UUID,
        username: str,
        mode: int = 0,
        ip_address: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Execute cross-switch operation (route camera to monitor)

        Args:
            camera_id: Camera ID
            monitor_id: Monitor ID
            user_id: User ID executing the operation
            username: Username executing the operation
            mode: Cross-switch mode (default: 0)
            ip_address: Client IP address for audit logging

        Returns:
            Dictionary with success status, message, and route info

        Raises:
            Exception: If SDK Bridge communication fails
        """
        logger.info("crossswitch_execute_request",
                   camera_id=camera_id,
                   monitor_id=monitor_id,
                   user_id=str(user_id),
                   username=username,
                   mode=mode)

        # First, clear any existing route for this monitor
        await self._clear_monitor_routes(monitor_id, user_id)

        # Execute cross-switch via SDK Bridge
        try:
            success = await sdk_bridge_client.execute_crossswitch(
                camera_id=camera_id,
                monitor_id=monitor_id,
                mode=mode
            )

            sdk_success = True
            sdk_error = None

        except Exception as e:
            logger.error("crossswitch_sdk_failed",
                        camera_id=camera_id,
                        monitor_id=monitor_id,
                        error=str(e),
                        exc_info=True)
            sdk_success = False
            sdk_error = str(e)

        # Get camera and monitor names for details
        details = await self._get_route_details(camera_id, monitor_id)

        # Create database record
        route = CrossSwitchRoute.create_route(
            camera_id=camera_id,
            monitor_id=monitor_id,
            executed_by=user_id,
            mode=mode,
            sdk_success=sdk_success,
            sdk_error=sdk_error,
            details=details
        )

        self.db.add(route)
        await self.db.commit()
        await self.db.refresh(route)

        # Create audit log
        await self._create_audit_log(
            action="crossswitch.execute",
            target=f"camera:{camera_id}->monitor:{monitor_id}",
            outcome="success" if sdk_success else "failure",
            details={
                "camera_id": camera_id,
                "monitor_id": monitor_id,
                "mode": mode,
                "sdk_success": sdk_success,
                "sdk_error": sdk_error
            },
            user_id=user_id,
            ip_address=ip_address
        )

        # Invalidate caches
        await redis_client.delete("monitors:list")
        await redis_client.delete(f"monitors:detail:{monitor_id}")

        if not sdk_success:
            logger.error("crossswitch_failed",
                        camera_id=camera_id,
                        monitor_id=monitor_id,
                        error=sdk_error)
            raise Exception(f"Cross-switch failed: {sdk_error}")

        logger.info("crossswitch_success",
                   camera_id=camera_id,
                   monitor_id=monitor_id,
                   route_id=str(route.id))

        return {
            "success": True,
            "message": f"Successfully switched camera {camera_id} to monitor {monitor_id}",
            "route": {
                "id": str(route.id),
                "camera_id": route.camera_id,
                "monitor_id": route.monitor_id,
                "mode": route.mode,
                "executed_at": route.executed_at,
                "executed_by": str(route.executed_by),
                "executed_by_username": username,
                "is_active": bool(route.is_active),
                "camera_name": details.get("camera_name"),
                "monitor_name": details.get("monitor_name")
            }
        }

    async def clear_monitor(
        self,
        monitor_id: int,
        user_id: uuid.UUID,
        username: str,
        ip_address: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Clear monitor (remove camera from monitor)

        Args:
            monitor_id: Monitor ID to clear
            user_id: User ID executing the operation
            username: Username executing the operation
            ip_address: Client IP address for audit logging

        Returns:
            Dictionary with success status and message

        Raises:
            Exception: If SDK Bridge communication fails
        """
        logger.info("clear_monitor_request",
                   monitor_id=monitor_id,
                   user_id=str(user_id),
                   username=username)

        # Execute clear via SDK Bridge
        try:
            success = await sdk_bridge_client.clear_monitor(monitor_id)
            sdk_success = True
            sdk_error = None

        except Exception as e:
            logger.error("clear_monitor_sdk_failed",
                        monitor_id=monitor_id,
                        error=str(e),
                        exc_info=True)
            sdk_success = False
            sdk_error = str(e)

        # Mark existing routes as cleared in database
        await self._clear_monitor_routes(monitor_id, user_id)

        # Create audit log
        await self._create_audit_log(
            action="crossswitch.clear",
            target=f"monitor:{monitor_id}",
            outcome="success" if sdk_success else "failure",
            details={
                "monitor_id": monitor_id,
                "sdk_success": sdk_success,
                "sdk_error": sdk_error
            },
            user_id=user_id,
            ip_address=ip_address
        )

        # Invalidate caches
        await redis_client.delete("monitors:list")
        await redis_client.delete(f"monitors:detail:{monitor_id}")

        if not sdk_success:
            logger.error("clear_monitor_failed",
                        monitor_id=monitor_id,
                        error=sdk_error)
            raise Exception(f"Clear monitor failed: {sdk_error}")

        logger.info("clear_monitor_success", monitor_id=monitor_id)

        return {
            "success": True,
            "message": f"Successfully cleared monitor {monitor_id}",
            "monitor_id": monitor_id
        }

    async def get_routing_state(self) -> Dict[str, Any]:
        """
        Get current routing state (active routes)

        Returns:
            Dictionary with list of active routes
        """
        logger.info("get_routing_state_request")

        # Query active routes from database
        result = await self.db.execute(
            select(CrossSwitchRoute)
            .where(CrossSwitchRoute.is_active == 1)
            .order_by(desc(CrossSwitchRoute.executed_at))
        )
        routes = result.scalars().all()

        # Transform to response format
        routes_list = [
            {
                "id": str(route.id),
                "camera_id": route.camera_id,
                "monitor_id": route.monitor_id,
                "mode": route.mode,
                "executed_at": route.executed_at,
                "executed_by": str(route.executed_by) if route.executed_by else None,
                "is_active": bool(route.is_active),
                "camera_name": route.details.get("camera_name") if route.details else None,
                "monitor_name": route.details.get("monitor_name") if route.details else None
            }
            for route in routes
        ]

        logger.info("get_routing_state_response", count=len(routes_list))

        return {
            "routes": routes_list,
            "total": len(routes_list)
        }

    async def get_routing_history(
        self,
        limit: int = 100,
        offset: int = 0,
        camera_id: Optional[int] = None,
        monitor_id: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        Get routing history (all routes including cleared)

        Args:
            limit: Maximum number of records to return
            offset: Number of records to skip
            camera_id: Filter by camera ID (optional)
            monitor_id: Filter by monitor ID (optional)

        Returns:
            Dictionary with historical routes and pagination info
        """
        logger.info("get_routing_history_request",
                   limit=limit,
                   offset=offset,
                   camera_id=camera_id,
                   monitor_id=monitor_id)

        # Build query with optional filters
        query = select(CrossSwitchRoute).order_by(desc(CrossSwitchRoute.executed_at))

        if camera_id is not None:
            query = query.where(CrossSwitchRoute.camera_id == camera_id)

        if monitor_id is not None:
            query = query.where(CrossSwitchRoute.monitor_id == monitor_id)

        # Get total count
        count_result = await self.db.execute(query)
        total = len(count_result.scalars().all())

        # Apply pagination
        query = query.limit(limit).offset(offset)

        result = await self.db.execute(query)
        routes = result.scalars().all()

        # Transform to response format
        history_list = [route.to_dict() for route in routes]

        logger.info("get_routing_history_response",
                   count=len(history_list),
                   total=total)

        return {
            "history": history_list,
            "total": total,
            "limit": limit,
            "offset": offset
        }

    async def _clear_monitor_routes(self, monitor_id: int, cleared_by: uuid.UUID) -> None:
        """
        Mark all active routes for a monitor as cleared

        Args:
            monitor_id: Monitor ID
            cleared_by: User ID who is clearing the routes
        """
        result = await self.db.execute(
            select(CrossSwitchRoute)
            .where(and_(
                CrossSwitchRoute.monitor_id == monitor_id,
                CrossSwitchRoute.is_active == 1
            ))
        )
        active_routes = result.scalars().all()

        for route in active_routes:
            route.clear_route(cleared_by)

        if active_routes:
            await self.db.commit()
            logger.info("monitor_routes_cleared",
                       monitor_id=monitor_id,
                       count=len(active_routes))

    async def _get_route_details(self, camera_id: int, monitor_id: int) -> Dict[str, Any]:
        """
        Get additional details for route (camera/monitor names)

        Args:
            camera_id: Camera ID
            monitor_id: Monitor ID

        Returns:
            Dictionary with camera and monitor names
        """
        details = {}

        try:
            # Get camera name (from cache if available)
            camera_data = await redis_client.get_json(f"cameras:detail:{camera_id}")
            if camera_data:
                details["camera_name"] = camera_data.get("name")

            # Get monitor name (from cache if available)
            monitor_data = await redis_client.get_json(f"monitors:detail:{monitor_id}")
            if monitor_data:
                details["monitor_name"] = monitor_data.get("name")

        except Exception as e:
            logger.warning("failed_to_get_route_details", error=str(e))

        return details

    async def _create_audit_log(
        self,
        action: str,
        target: str,
        outcome: str,
        details: Optional[Dict[str, Any]] = None,
        user_id: Optional[uuid.UUID] = None,
        ip_address: Optional[str] = None
    ) -> None:
        """
        Create audit log entry

        Args:
            action: Action name
            target: Target of action
            outcome: Outcome (success, failure, error)
            details: Additional details
            user_id: User ID
            ip_address: Client IP address
        """
        try:
            audit_log = AuditLog(
                user_id=user_id,
                action=action,
                target=target,
                outcome=outcome,
                details=details,
                ip_address=ip_address
            )
            self.db.add(audit_log)
            await self.db.commit()
        except Exception as e:
            logger.error("audit_log_creation_failed", action=action, error=str(e))
            await self.db.rollback()
