#!/usr/bin/env python3
"""
Homebridge MCP Server
Control Homebridge smart home devices via Claude Code.

Features:
- List all accessories/devices
- Get device status and characteristics
- Control devices (on/off, brightness, temperature, etc.)
- View Homebridge logs
- Manage Homebridge service
"""

import asyncio
import json
import os
import subprocess
from pathlib import Path
from typing import Any, Optional
from datetime import datetime

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

# Homebridge paths
HOMEBRIDGE_DIR = Path.home() / ".homebridge"
CONFIG_FILE = HOMEBRIDGE_DIR / "config.json"
ACCESSORIES_FILE = HOMEBRIDGE_DIR / "accessories" / "cachedAccessories"
PERSIST_DIR = HOMEBRIDGE_DIR / "persist"
LOG_FILE = HOMEBRIDGE_DIR / "homebridge.log"

# Initialize MCP server
server = Server("homebridge-mcp")


def load_config() -> dict:
    """Load Homebridge configuration."""
    if CONFIG_FILE.exists():
        with open(CONFIG_FILE) as f:
            return json.load(f)
    return {}


def load_cached_accessories() -> list:
    """Load cached accessories from Homebridge."""
    if ACCESSORIES_FILE.exists():
        with open(ACCESSORIES_FILE) as f:
            return json.load(f)
    return []


def get_accessory_info(accessory: dict) -> dict:
    """Extract useful info from accessory data."""
    info = {}
    device_chars = {}

    # Check for LG ThinQ format (context.device.data structure)
    context = accessory.get("context", {})
    if context and "device" in context:
        device = context.get("device", {})
        device_data = device.get("data", {})

        # Extract device info from LG ThinQ format
        info["name"] = device_data.get("alias", "Unknown Device")
        info["manufacturer"] = "LG"
        info["model"] = device_data.get("modelName", "Unknown")
        info["device_type"] = device_data.get("deviceType", "")

        # Extract device state from snapshot
        snapshot = device_data.get("snapshot", {})

        # Refrigerator state
        if "refState" in snapshot:
            ref_state = snapshot["refState"]
            device_chars["fridge_temp"] = f"{ref_state.get('fridgeTemp', 'N/A')}°C"
            device_chars["freezer_temp"] = f"{ref_state.get('freezerTemp', 'N/A')}°C"
            device_chars["status"] = ref_state.get("atLeastOneDoorOpen", "unknown")
            if "fridgeStatus" in ref_state:
                device_chars["fridge_status"] = ref_state["fridgeStatus"]

        # Washer/Dryer state
        if "washerDryer" in snapshot:
            washer_state = snapshot["washerDryer"]
            device_chars["state"] = washer_state.get("state", "unknown")
            if "remainTimeHour" in washer_state:
                device_chars["remaining_time"] = f"{washer_state.get('remainTimeHour', 0)}h {washer_state.get('remainTimeMinute', 0)}m"
            if "course" in washer_state:
                device_chars["course"] = washer_state["course"]

        # Air conditioner state
        if "airState" in snapshot:
            air_state = snapshot["airState"]
            if "tempState" in air_state:
                temp_state = air_state["tempState"]
                device_chars["current_temp"] = f"{temp_state.get('current', 'N/A')}°C"
                device_chars["target_temp"] = f"{temp_state.get('target', 'N/A')}°C"
            if "operation" in air_state:
                device_chars["power"] = "on" if air_state["operation"] else "off"

        # Generic online status
        if "online" in device_data:
            device_chars["online"] = "yes" if device_data["online"] else "no"

        info["characteristics"] = device_chars
        info["uuid"] = accessory.get("UUID", "")
        info["aid"] = accessory.get("aid", "")
        return info

    # Fallback to HAP-style accessories format
    services = accessory.get("services", [])

    # Get accessory information service
    for service in services:
        service_type = service.get("type", "")
        characteristics = service.get("characteristics", [])

        if "AccessoryInformation" in service_type or service_type == "3E":
            for char in characteristics:
                char_type = char.get("type", "")
                value = char.get("value")

                if "Name" in char_type or char_type == "23":
                    info["name"] = value
                elif "Manufacturer" in char_type or char_type == "20":
                    info["manufacturer"] = value
                elif "Model" in char_type or char_type == "21":
                    info["model"] = value
                elif "SerialNumber" in char_type or char_type == "30":
                    info["serial"] = value

    # Get device-specific characteristics
    for service in services:
        service_type = service.get("type", "")
        characteristics = service.get("characteristics", [])

        # Skip accessory information service
        if "AccessoryInformation" in service_type or service_type == "3E":
            continue

        for char in characteristics:
            char_type = char.get("type", "")
            char_desc = char.get("description", char_type)
            value = char.get("value")

            if value is not None:
                # Map common characteristic types
                if "On" in char_type or char_type == "25":
                    device_chars["power"] = "on" if value else "off"
                elif "Brightness" in char_type or char_type == "8":
                    device_chars["brightness"] = f"{value}%"
                elif "CurrentTemperature" in char_type or char_type == "11":
                    device_chars["current_temperature"] = f"{value}°C"
                elif "TargetTemperature" in char_type or char_type == "35":
                    device_chars["target_temperature"] = f"{value}°C"
                elif "CurrentHeatingCoolingState" in char_type:
                    states = {0: "off", 1: "heating", 2: "cooling"}
                    device_chars["heating_cooling_state"] = states.get(value, str(value))
                elif "Active" in char_type or char_type == "B0":
                    device_chars["active"] = "active" if value else "inactive"
                elif "ContactSensorState" in char_type:
                    device_chars["contact"] = "open" if value else "closed"
                elif "MotionDetected" in char_type:
                    device_chars["motion"] = "detected" if value else "none"
                elif char_desc and value is not None:
                    device_chars[char_desc.lower().replace(" ", "_")] = value

    info["characteristics"] = device_chars
    info["uuid"] = accessory.get("UUID", "")
    info["aid"] = accessory.get("aid", "")

    # Set defaults if not found
    if "name" not in info:
        info["name"] = "Unknown Device"
    if "manufacturer" not in info:
        info["manufacturer"] = "Unknown"

    return info


def get_service_status() -> dict:
    """Get Homebridge service status."""
    try:
        result = subprocess.run(
            ["launchctl", "list"],
            capture_output=True,
            text=True
        )

        for line in result.stdout.split("\n"):
            if "homebridge" in line.lower():
                parts = line.split()
                if len(parts) >= 3:
                    pid = parts[0]
                    exit_code = parts[1]
                    label = parts[2]

                    return {
                        "running": pid != "-",
                        "pid": pid if pid != "-" else None,
                        "exit_code": exit_code,
                        "label": label
                    }

        return {"running": False, "error": "Service not found"}
    except Exception as e:
        return {"running": False, "error": str(e)}


def get_recent_logs(lines: int = 50) -> str:
    """Get recent Homebridge logs."""
    if not LOG_FILE.exists():
        return "Log file not found"

    try:
        result = subprocess.run(
            ["tail", f"-{lines}", str(LOG_FILE)],
            capture_output=True,
            text=True
        )
        # Filter out QR code escape sequences
        log_lines = []
        for line in result.stdout.split("\n"):
            if not line.startswith("[47m") and not line.startswith("[40m"):
                log_lines.append(line)
        return "\n".join(log_lines)
    except Exception as e:
        return f"Error reading logs: {e}"


def restart_homebridge() -> dict:
    """Restart Homebridge service."""
    try:
        # Stop
        subprocess.run(
            ["launchctl", "stop", "com.homebridge.server"],
            capture_output=True
        )

        # Wait a moment
        import time
        time.sleep(2)

        # Start
        subprocess.run(
            ["launchctl", "start", "com.homebridge.server"],
            capture_output=True
        )

        time.sleep(3)

        return {
            "success": True,
            "message": "Homebridge restarted successfully",
            "status": get_service_status()
        }
    except Exception as e:
        return {"success": False, "error": str(e)}


# ============================================================================
# MCP Tools
# ============================================================================

@server.list_tools()
async def list_tools() -> list[Tool]:
    """List available Homebridge control tools."""
    return [
        Tool(
            name="homebridge_list_devices",
            description="List all Homebridge accessories/devices with their current status",
            inputSchema={
                "type": "object",
                "properties": {},
                "required": []
            }
        ),
        Tool(
            name="homebridge_get_device",
            description="Get detailed information about a specific device by name or UUID",
            inputSchema={
                "type": "object",
                "properties": {
                    "device": {
                        "type": "string",
                        "description": "Device name or UUID"
                    }
                },
                "required": ["device"]
            }
        ),
        Tool(
            name="homebridge_status",
            description="Get Homebridge service status and configuration",
            inputSchema={
                "type": "object",
                "properties": {},
                "required": []
            }
        ),
        Tool(
            name="homebridge_logs",
            description="Get recent Homebridge logs",
            inputSchema={
                "type": "object",
                "properties": {
                    "lines": {
                        "type": "integer",
                        "description": "Number of log lines to retrieve (default: 50)",
                        "default": 50
                    }
                },
                "required": []
            }
        ),
        Tool(
            name="homebridge_restart",
            description="Restart the Homebridge service",
            inputSchema={
                "type": "object",
                "properties": {},
                "required": []
            }
        ),
        Tool(
            name="homebridge_config",
            description="View or update Homebridge configuration",
            inputSchema={
                "type": "object",
                "properties": {
                    "action": {
                        "type": "string",
                        "enum": ["view", "get_platforms", "get_accessories"],
                        "description": "Action to perform",
                        "default": "view"
                    }
                },
                "required": []
            }
        ),
        Tool(
            name="homebridge_search_device",
            description="Search for devices by name, type, or manufacturer",
            inputSchema={
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Search query (matches name, type, manufacturer)"
                    }
                },
                "required": ["query"]
            }
        )
    ]


@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    """Handle tool calls."""

    if name == "homebridge_list_devices":
        accessories = load_cached_accessories()

        if not accessories:
            return [TextContent(
                type="text",
                text="No devices found. Homebridge may still be starting up or no accessories are configured."
            )]

        devices = []
        for acc in accessories:
            info = get_accessory_info(acc)
            device_summary = {
                "name": info.get("name", "Unknown"),
                "manufacturer": info.get("manufacturer", "Unknown"),
                "model": info.get("model", "Unknown"),
                "uuid": info.get("uuid", "")[:8] + "...",
            }

            # Add key characteristics
            chars = info.get("characteristics", {})
            if chars:
                device_summary["status"] = chars

            devices.append(device_summary)

        result = {
            "device_count": len(devices),
            "devices": devices
        }

        return [TextContent(
            type="text",
            text=json.dumps(result, indent=2)
        )]

    elif name == "homebridge_get_device":
        device_query = arguments.get("device", "").lower()
        accessories = load_cached_accessories()

        for acc in accessories:
            info = get_accessory_info(acc)
            name = info.get("name", "").lower()
            uuid = info.get("uuid", "").lower()

            if device_query in name or device_query in uuid:
                return [TextContent(
                    type="text",
                    text=json.dumps(info, indent=2)
                )]

        return [TextContent(
            type="text",
            text=f"Device '{arguments.get('device')}' not found"
        )]

    elif name == "homebridge_status":
        service_status = get_service_status()
        config = load_config()

        status = {
            "service": service_status,
            "bridge": config.get("bridge", {}),
            "platform_count": len(config.get("platforms", [])),
            "accessory_count": len(config.get("accessories", [])),
            "config_path": str(CONFIG_FILE),
            "log_path": str(LOG_FILE)
        }

        return [TextContent(
            type="text",
            text=json.dumps(status, indent=2)
        )]

    elif name == "homebridge_logs":
        lines = arguments.get("lines", 50)
        logs = get_recent_logs(lines)

        return [TextContent(
            type="text",
            text=f"=== Homebridge Logs (last {lines} lines) ===\n\n{logs}"
        )]

    elif name == "homebridge_restart":
        result = restart_homebridge()

        return [TextContent(
            type="text",
            text=json.dumps(result, indent=2)
        )]

    elif name == "homebridge_config":
        action = arguments.get("action", "view")
        config = load_config()

        if action == "view":
            # Redact sensitive info
            safe_config = config.copy()
            for platform in safe_config.get("platforms", []):
                if "password" in platform:
                    platform["password"] = "***REDACTED***"
                if "token" in platform:
                    platform["token"] = "***REDACTED***"

            return [TextContent(
                type="text",
                text=json.dumps(safe_config, indent=2)
            )]

        elif action == "get_platforms":
            platforms = []
            for p in config.get("platforms", []):
                platforms.append({
                    "platform": p.get("platform"),
                    "name": p.get("name")
                })
            return [TextContent(
                type="text",
                text=json.dumps({"platforms": platforms}, indent=2)
            )]

        elif action == "get_accessories":
            return [TextContent(
                type="text",
                text=json.dumps({"accessories": config.get("accessories", [])}, indent=2)
            )]

        return [TextContent(type="text", text="Unknown action")]

    elif name == "homebridge_search_device":
        query = arguments.get("query", "").lower()
        accessories = load_cached_accessories()

        matches = []
        for acc in accessories:
            info = get_accessory_info(acc)

            # Search in various fields
            searchable = " ".join([
                info.get("name", ""),
                info.get("manufacturer", ""),
                info.get("model", ""),
                str(info.get("characteristics", {}))
            ]).lower()

            if query in searchable:
                matches.append({
                    "name": info.get("name"),
                    "manufacturer": info.get("manufacturer"),
                    "model": info.get("model"),
                    "uuid": info.get("uuid", "")[:8] + "...",
                    "status": info.get("characteristics", {})
                })

        return [TextContent(
            type="text",
            text=json.dumps({
                "query": query,
                "matches": len(matches),
                "devices": matches
            }, indent=2)
        )]

    return [TextContent(type="text", text=f"Unknown tool: {name}")]


async def run_server():
    """Run the MCP server."""
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            server.create_initialization_options()
        )


def main():
    """Main entry point."""
    asyncio.run(run_server())


if __name__ == "__main__":
    main()
