"""
Camera service for managing camera discovery and information
"""
from typing import List, Optional, Dict, Any
from datetime import datetime
import structlog

from clients.sdk_bridge_client import sdk_bridge_client
from clients.redis_client import redis_client
from config import settings

logger = structlog.get_logger()

# Redis cache TTL for camera data (60 seconds)
CAMERA_CACHE_TTL = 60


class CameraService:
    """Service for camera operations"""

    def __init__(self):
        """Initialize camera service"""
        pass

    async def list_cameras(self, use_cache: bool = True) -> Dict[str, Any]:
        """
        Get list of all cameras from SDK Bridge

        Args:
            use_cache: Whether to use Redis cache (default: True)

        Returns:
            Dictionary with 'cameras' list and 'total' count
        """
        cache_key = "cameras:list"

        # Try to get from cache first
        if use_cache:
            cached_data = await redis_client.get_json(cache_key)
            if cached_data:
                logger.info("camera_list_cache_hit")
                return cached_data

        logger.info("camera_list_cache_miss_fetching_from_sdk")

        try:
            # Fetch cameras from SDK Bridge via gRPC
            cameras = await sdk_bridge_client.list_cameras()

            # Transform to response format
            result = {
                "cameras": cameras,
                "total": len(cameras)
            }

            # Cache the result
            if use_cache:
                await redis_client.set_json(cache_key, result, expire=CAMERA_CACHE_TTL)
                logger.info("camera_list_cached", count=len(cameras), ttl=CAMERA_CACHE_TTL)

            return result

        except Exception as e:
            logger.error("camera_list_failed", error=str(e), exc_info=True)
            # Return empty list on error
            return {"cameras": [], "total": 0}

    async def get_camera(self, camera_id: int, use_cache: bool = True) -> Optional[Dict[str, Any]]:
        """
        Get single camera by ID

        Args:
            camera_id: Camera ID (channel number)
            use_cache: Whether to use Redis cache (default: True)

        Returns:
            Camera dictionary or None if not found
        """
        cache_key = f"cameras:detail:{camera_id}"

        # Try to get from cache first
        if use_cache:
            cached_data = await redis_client.get_json(cache_key)
            if cached_data:
                logger.info("camera_detail_cache_hit", camera_id=camera_id)
                return cached_data

        logger.info("camera_detail_cache_miss_fetching_from_sdk", camera_id=camera_id)

        try:
            # Fetch camera from SDK Bridge via gRPC
            camera = await sdk_bridge_client.get_camera(camera_id)

            if not camera:
                logger.warning("camera_not_found", camera_id=camera_id)
                return None

            # Cache the result
            if use_cache:
                await redis_client.set_json(cache_key, camera, expire=CAMERA_CACHE_TTL)
                logger.info("camera_detail_cached", camera_id=camera_id, ttl=CAMERA_CACHE_TTL)

            return camera

        except Exception as e:
            logger.error("camera_detail_failed", camera_id=camera_id, error=str(e), exc_info=True)
            return None

    async def invalidate_cache(self, camera_id: Optional[int] = None) -> None:
        """
        Invalidate camera cache

        Args:
            camera_id: Specific camera ID to invalidate, or None to invalidate all
        """
        if camera_id is not None:
            # Invalidate specific camera
            cache_key = f"cameras:detail:{camera_id}"
            await redis_client.delete(cache_key)
            logger.info("camera_cache_invalidated", camera_id=camera_id)
        else:
            # Invalidate camera list cache
            await redis_client.delete("cameras:list")
            logger.info("camera_list_cache_invalidated")

    async def refresh_camera_list(self) -> Dict[str, Any]:
        """
        Force refresh camera list from SDK Bridge (bypass cache)

        Returns:
            Dictionary with 'cameras' list and 'total' count
        """
        logger.info("camera_list_force_refresh")

        # Invalidate cache first
        await self.invalidate_cache()

        # Fetch fresh data
        return await self.list_cameras(use_cache=False)

    async def get_camera_count(self) -> int:
        """
        Get total number of cameras

        Returns:
            Total camera count
        """
        result = await self.list_cameras(use_cache=True)
        return result["total"]

    async def search_cameras(self, query: str) -> List[Dict[str, Any]]:
        """
        Search cameras by name or description

        Args:
            query: Search query string

        Returns:
            List of matching cameras
        """
        result = await self.list_cameras(use_cache=True)
        cameras = result["cameras"]

        # Simple case-insensitive search
        query_lower = query.lower()
        matching = [
            cam for cam in cameras
            if query_lower in cam.get("name", "").lower()
            or query_lower in cam.get("description", "").lower()
        ]

        logger.info("camera_search", query=query, matches=len(matching))
        return matching

    async def get_online_cameras(self) -> List[Dict[str, Any]]:
        """
        Get list of online cameras only

        Returns:
            List of online cameras
        """
        result = await self.list_cameras(use_cache=True)
        cameras = result["cameras"]

        online = [cam for cam in cameras if cam.get("status") == "online"]

        logger.info("online_cameras_retrieved", count=len(online), total=len(cameras))
        return online

    async def get_ptz_cameras(self) -> List[Dict[str, Any]]:
        """
        Get list of cameras with PTZ capabilities

        Returns:
            List of PTZ cameras
        """
        result = await self.list_cameras(use_cache=True)
        cameras = result["cameras"]

        ptz_cameras = [cam for cam in cameras if cam.get("has_ptz", False)]

        logger.info("ptz_cameras_retrieved", count=len(ptz_cameras), total=len(cameras))
        return ptz_cameras
