"""Scene understanding and visual perception.

Provides camera image interpretation and scene description.
This is an optional feature that can work with or without external vision APIs.

Fulfills SKILL promises like:
- "What do you see?"
- "Describe the room"
- "Is there anything in front?"
"""

import base64
from dataclasses import dataclass
from enum import Enum
from typing import Any


class PerceptionBackend(Enum):
    """Available perception backends."""

    NONE = "none"  # No vision API, basic metadata only
    CLAUDE = "claude"  # Claude vision API
    OPENAI = "openai"  # GPT-4V API
    LOCAL = "local"  # Local vision model (future)


@dataclass
class DetectedObject:
    """An object detected in the scene."""

    label: str
    confidence: float
    bounding_box: dict[str, int] | None = None  # x, y, width, height
    distance_estimate: float | None = None  # meters


@dataclass
class SceneDescription:
    """Description of a scene."""

    summary: str
    objects: list[DetectedObject]
    spatial_layout: str
    hazards: list[str]
    confidence: float


class SceneUnderstanding:
    """Scene understanding and visual perception.

    Provides:
    - Camera image capture
    - Scene description (with optional vision API)
    - Object detection
    - Spatial analysis

    Note: Without vision API, provides basic metadata only.
    """

    def __init__(
        self, backend: PerceptionBackend = PerceptionBackend.NONE, api_key: str | None = None
    ):
        """Initialize scene understanding.

        Args:
            backend: Vision API backend to use
            api_key: API key for vision service (if using external API)
        """
        self.backend = backend
        self.api_key = api_key
        self._vision_client = None

        if backend != PerceptionBackend.NONE and api_key:
            self._init_vision_client()

    def _init_vision_client(self):
        """Initialize vision API client."""
        if self.backend == PerceptionBackend.CLAUDE:
            try:
                import anthropic

                self._vision_client = anthropic.Anthropic(api_key=self.api_key)
            except ImportError:
                print(
                    "Warning: anthropic package not installed. "
                    "Scene understanding will use basic mode."
                )
                self.backend = PerceptionBackend.NONE

        elif self.backend == PerceptionBackend.OPENAI:
            try:
                import openai

                self._vision_client = openai.OpenAI(api_key=self.api_key)
            except ImportError:
                print(
                    "Warning: openai package not installed. "
                    "Scene understanding will use basic mode."
                )
                self.backend = PerceptionBackend.NONE

    async def describe_scene(self, image_data: bytes, camera_info: dict = None) -> SceneDescription:
        """Generate description of scene from camera image.

        Args:
            image_data: Raw image bytes
            camera_info: Camera metadata (topic, timestamp, etc.)

        Returns:
            Scene description
        """
        if self.backend == PerceptionBackend.NONE or not self._vision_client:
            return self._describe_basic(camera_info)

        if self.backend == PerceptionBackend.CLAUDE:
            return await self._describe_with_claude(image_data, camera_info)

        if self.backend == PerceptionBackend.OPENAI:
            return await self._describe_with_openai(image_data, camera_info)

        return self._describe_basic(camera_info)

    def _describe_basic(self, camera_info: dict = None) -> SceneDescription:
        """Generate basic description without vision API.

        This provides metadata only when no vision API is available.
        """
        info = camera_info or {}
        topic = info.get("topic", "/camera/image_raw")
        timestamp = info.get("timestamp", "unknown")

        summary = f"Camera image from {topic} at {timestamp}"

        return SceneDescription(
            summary=summary,
            objects=[],
            spatial_layout="No spatial analysis available (vision API not configured)",
            hazards=[],
            confidence=0.0,
        )

    async def _describe_with_claude(self, image_data: bytes, camera_info: dict) -> SceneDescription:
        """Generate description using Claude vision API."""
        try:
            # Encode image to base64
            image_b64 = base64.b64encode(image_data).decode("utf-8")

            # Determine image format (assume JPEG for now)
            media_type = "image/jpeg"

            response = self._vision_client.messages.create(
                model="claude-3-opus-20240229",
                max_tokens=500,
                messages=[
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "image",
                                "source": {
                                    "type": "base64",
                                    "media_type": media_type,
                                    "data": image_b64,
                                },
                            },
                            {
                                "type": "text",
                                "text": """Describe what you see in this robot camera image.

Provide:
1. A brief summary of the scene
2. List of visible objects
3. Spatial layout (what's in front, sides, etc.)
4. Any potential hazards or obstacles

Be concise but informative.""",
                            },
                        ],
                    }
                ],
            )

            description_text = response.content[0].text

            # Parse the response (simple parsing)
            return self._parse_description(description_text)

        except Exception as e:
            print(f"Vision API error: {e}")
            return self._describe_basic(camera_info)

    async def _describe_with_openai(self, image_data: bytes, camera_info: dict) -> SceneDescription:
        """Generate description using GPT-4V API."""
        try:

            image_b64 = base64.b64encode(image_data).decode("utf-8")

            response = self._vision_client.chat.completions.create(
                model="gpt-4-vision-preview",
                max_tokens=500,
                messages=[
                    {
                        "role": "user",
                        "content": [
                            {
                                "type": "text",
                                "text": "Describe what you see in this robot camera image. "
                                "List objects, spatial layout, and any hazards.",
                            },
                            {
                                "type": "image_url",
                                "image_url": {"url": f"data:image/jpeg;base64,{image_b64}"},
                            },
                        ],
                    }
                ],
            )

            description_text = response.choices[0].message.content
            return self._parse_description(description_text)

        except Exception as e:
            print(f"Vision API error: {e}")
            return self._describe_basic(camera_info)

    def _parse_description(self, text: str) -> SceneDescription:
        """Parse vision API response into structured description."""
        # Simple parsing - in production would use more sophisticated NLP

        lines = text.strip().split("\n")

        # First non-empty line is summary
        summary = next((l for l in lines if l.strip()), "Scene observed")

        # Look for object mentions
        objects = []
        hazard_keywords = ["obstacle", "hazard", "danger", "blocked", "person"]
        hazards = []

        for line in lines:
            line_lower = line.lower()

            # Check for hazards
            for hazard in hazard_keywords:
                if hazard in line_lower:
                    hazards.append(line.strip())
                    break

            # Simple object extraction (would be better with NLP)
            if any(word in line_lower for word in ["see", "visible", "there is", "there are"]):
                # Extract potential object names
                pass  # Would need NLP for proper extraction

        return SceneDescription(
            summary=summary,
            objects=objects,
            spatial_layout="See summary for spatial details",
            hazards=hazards,
            confidence=0.8 if self.backend != PerceptionBackend.NONE else 0.0,
        )

    async def answer_query(
        self, query: str, image_data: bytes = None, camera_info: dict = None
    ) -> str:
        """Answer a specific query about the scene.

        Args:
            query: Natural language query (e.g., "What's in front?")
            image_data: Optional camera image
            camera_info: Camera metadata

        Returns:
            Answer to the query
        """
        query_lower = query.lower()

        # Handle specific queries without vision API
        if "what do you see" in query_lower or "describe" in query_lower:
            if image_data and self.backend != PerceptionBackend.NONE:
                desc = await self.describe_scene(image_data, camera_info)
                return desc.summary
            else:
                return (
                    "I can see the camera feed, but detailed scene understanding "
                    "requires a vision API (Claude or OpenAI). "
                    "Camera is operational and capturing images."
                )

        if "in front" in query_lower:
            return (
                "Forward sensor data would be needed for obstacle detection. "
                "Use LiDAR or depth camera for precise front detection."
            )

        if "distance" in query_lower or "how far" in query_lower:
            return "Distance estimation requires LiDAR or depth camera data."

        if "object" in query_lower or "see" in query_lower:
            return "Object detection requires vision API configuration."

        # Generic response
        return (
            "I can access camera data but detailed visual analysis requires "
            "a vision API. Basic camera operations are functional."
        )

    def get_perception_status(self) -> dict[str, Any]:
        """Get status of perception system.

        Returns:
            Status information
        """
        return {
            "backend": self.backend.value,
            "available": self.backend != PerceptionBackend.NONE,
            "api_configured": self.api_key is not None,
            "client_initialized": self._vision_client is not None,
            "capabilities": ["image_capture", "basic_metadata"]
            + (
                ["scene_description", "object_detection"]
                if self.backend != PerceptionBackend.NONE
                else []
            ),
        }


# Convenience functions for SKILL fulfillment


async def what_do_you_see(scene: SceneUnderstanding, image_data: bytes = None) -> str:
    """Answer 'What do you see?' query.

    Convenience function for SKILL fulfillment.
    """
    if image_data:
        desc = await scene.describe_scene(image_data)
        return desc.summary
    else:
        return "Camera is active. Capture an image to analyze the scene."


async def describe_the_room(scene: SceneUnderstanding, image_data: bytes = None) -> str:
    """Answer 'Describe the room' query.

    Convenience function for SKILL fulfillment.
    """
    if image_data:
        desc = await scene.describe_scene(image_data)
        return f"{desc.summary}\n\nObjects: {', '.join(o.label for o in desc.objects)}"
    else:
        return "Room description requires camera image capture."


def is_path_clear(lidar_data: list[float], threshold: float = 0.5) -> bool:
    """Check if path is clear based on LiDAR data.

    Args:
        lidar_data: Distance readings
        threshold: Minimum clear distance in meters

    Returns:
        True if path is clear
    """
    if not lidar_data:
        return True  # Assume clear if no data

    # Check forward sector (center of LiDAR sweep)
    center_idx = len(lidar_data) // 2
    forward_sector = lidar_data[center_idx - 10 : center_idx + 10]

    return all(d > threshold for d in forward_sector if d > 0)
