# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Home Assistant Skill

Control your smart home via Home Assistant REST API.

Setup:
    HOMEASSISTANT_URL=http://homeassistant.local:8123
    HOMEASSISTANT_TOKEN=your_long_lived_access_token
"""

import logging
import os

import httpx

from familiar.core.utils import format_http_error

logger = logging.getLogger(__name__)

REQUEST_TIMEOUT = 30


# ---------------------------------------------------------------------------
# Configuration helpers
# ---------------------------------------------------------------------------


def _get_ha_config() -> dict:
    return {
        "url": os.environ.get("HOMEASSISTANT_URL", "").rstrip("/"),
        "token": os.environ.get("HOMEASSISTANT_TOKEN", ""),
    }


def _ha_configured() -> bool:
    cfg = _get_ha_config()
    return bool(cfg["url"] and cfg["token"])


def _ha_headers() -> dict:
    cfg = _get_ha_config()
    return {
        "Authorization": f"Bearer {cfg['token']}",
        "Content-Type": "application/json",
    }


# ---------------------------------------------------------------------------
# Tool handlers
# ---------------------------------------------------------------------------


def ha_list_entities(data: dict) -> str:
    """List entities, optionally filtered by domain."""
    if not _ha_configured():
        return "Home Assistant not configured. Run /connect homeassistant to set up, or set HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN."

    cfg = _get_ha_config()
    domain = data.get("domain", "")

    try:
        resp = httpx.get(
            f"{cfg['url']}/api/states",
            headers=_ha_headers(),
            timeout=REQUEST_TIMEOUT,
        )
        if resp.status_code != 200:
            return format_http_error(
                "Home Assistant", status_code=resp.status_code, connect_cmd="homeassistant"
            )

        entities = resp.json()
        if domain:
            entities = [e for e in entities if e.get("entity_id", "").startswith(f"{domain}.")]

        if not entities:
            msg = f"No entities found for domain '{domain}'." if domain else "No entities found."
            return msg

        lines = [f"Entities ({len(entities)}):"]
        for entity in entities[:50]:
            eid = entity.get("entity_id", "")
            state = entity.get("state", "unknown")
            name = entity.get("attributes", {}).get("friendly_name", eid)
            lines.append(f"  {name} ({eid}): {state}")

        if len(entities) > 50:
            lines.append(f"  ... and {len(entities) - 50} more")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        return format_http_error("Home Assistant", exception=e, connect_cmd="homeassistant")


def ha_get_state(data: dict) -> str:
    """Get the current state and attributes of an entity."""
    if not _ha_configured():
        return "Home Assistant not configured. Run /connect homeassistant to set up, or set HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN."

    entity_id = data.get("entity_id", "").strip()
    if not entity_id:
        return "Please provide an entity_id."

    cfg = _get_ha_config()

    try:
        resp = httpx.get(
            f"{cfg['url']}/api/states/{entity_id}",
            headers=_ha_headers(),
            timeout=REQUEST_TIMEOUT,
        )
        if resp.status_code == 404:
            return f"Entity not found: {entity_id}"
        if resp.status_code != 200:
            return format_http_error(
                "Home Assistant", status_code=resp.status_code, connect_cmd="homeassistant"
            )

        entity = resp.json()
        state = entity.get("state", "unknown")
        attrs = entity.get("attributes", {})
        name = attrs.get("friendly_name", entity_id)

        lines = [f"{name} ({entity_id}):", f"  State: {state}"]
        for key, value in attrs.items():
            if key != "friendly_name":
                lines.append(f"  {key}: {value}")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        return format_http_error("Home Assistant", exception=e, connect_cmd="homeassistant")


def ha_call_service(data: dict) -> str:
    """Call a Home Assistant service on a specific entity."""
    if not _ha_configured():
        return "Home Assistant not configured. Run /connect homeassistant to set up, or set HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN."

    domain = data.get("domain", "").strip()
    service = data.get("service", "").strip()
    entity_id = data.get("entity_id", "").strip()

    if not domain or not service:
        return "Please provide both 'domain' and 'service' parameters."

    cfg = _get_ha_config()
    body = {}
    if entity_id:
        body["entity_id"] = entity_id

    try:
        resp = httpx.post(
            f"{cfg['url']}/api/services/{domain}/{service}",
            headers=_ha_headers(),
            json=body,
            timeout=REQUEST_TIMEOUT,
        )
        if resp.status_code != 200:
            return format_http_error(
                "Home Assistant", status_code=resp.status_code, connect_cmd="homeassistant"
            )

        target = entity_id if entity_id else f"{domain}.{service}"
        return f"Service called: {domain}/{service} on {target}"

    except httpx.HTTPError as e:
        return format_http_error("Home Assistant", exception=e, connect_cmd="homeassistant")


def ha_toggle_entity(data: dict) -> str:
    """Toggle an entity on/off."""
    if not _ha_configured():
        return "Home Assistant not configured. Run /connect homeassistant to set up, or set HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN."

    entity_id = data.get("entity_id", "").strip()
    if not entity_id:
        return "Please provide an entity_id."

    cfg = _get_ha_config()

    try:
        resp = httpx.post(
            f"{cfg['url']}/api/services/homeassistant/toggle",
            headers=_ha_headers(),
            json={"entity_id": entity_id},
            timeout=REQUEST_TIMEOUT,
        )
        if resp.status_code != 200:
            return format_http_error(
                "Home Assistant", status_code=resp.status_code, connect_cmd="homeassistant"
            )

        return f"Toggled: {entity_id}"

    except httpx.HTTPError as e:
        return format_http_error("Home Assistant", exception=e, connect_cmd="homeassistant")


def ha_get_history(data: dict) -> str:
    """Get entity history for a time period."""
    if not _ha_configured():
        return "Home Assistant not configured. Run /connect homeassistant to set up, or set HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN."

    entity_id = data.get("entity_id", "").strip()
    if not entity_id:
        return "Please provide an entity_id."

    timestamp = data.get("timestamp", "")
    cfg = _get_ha_config()

    url = f"{cfg['url']}/api/history/period"
    if timestamp:
        url += f"/{timestamp}"
    url += f"?filter_entity_id={entity_id}"

    try:
        resp = httpx.get(
            url,
            headers=_ha_headers(),
            timeout=REQUEST_TIMEOUT,
        )
        if resp.status_code != 200:
            return format_http_error(
                "Home Assistant", status_code=resp.status_code, connect_cmd="homeassistant"
            )

        history = resp.json()
        if not history or not history[0]:
            return f"No history found for {entity_id}."

        entries = history[0]
        lines = [f"History for {entity_id} ({len(entries)} entries):"]
        for entry in entries[:20]:
            state = entry.get("state", "unknown")
            last_changed = entry.get("last_changed", "")
            if last_changed:
                # Trim the microseconds and timezone for readability
                last_changed = last_changed[:19].replace("T", " ")
            lines.append(f"  {last_changed}: {state}")

        if len(entries) > 20:
            lines.append(f"  ... and {len(entries) - 20} more entries")
        return "\n".join(lines)

    except httpx.HTTPError as e:
        return format_http_error("Home Assistant", exception=e, connect_cmd="homeassistant")


def ha_create_automation(data: dict) -> str:
    """Create a new Home Assistant automation."""
    if not _ha_configured():
        return "Home Assistant not configured. Run /connect homeassistant to set up, or set HOMEASSISTANT_URL and HOMEASSISTANT_TOKEN."

    automation_id = data.get("id", "").strip()
    alias = data.get("alias", "").strip()
    description = data.get("description", "")
    trigger = data.get("trigger", [])
    action = data.get("action", [])

    if not automation_id:
        return "Please provide an automation 'id'."
    if not alias:
        return "Please provide an automation 'alias' (friendly name)."
    if not trigger:
        return "Please provide at least one 'trigger'."
    if not action:
        return "Please provide at least one 'action'."

    cfg = _get_ha_config()
    automation_config = {
        "alias": alias,
        "description": description,
        "trigger": trigger,
        "action": action,
        "mode": data.get("mode", "single"),
    }

    condition = data.get("condition", [])
    if condition:
        automation_config["condition"] = condition

    try:
        resp = httpx.post(
            f"{cfg['url']}/api/config/automation/config/{automation_id}",
            headers=_ha_headers(),
            json=automation_config,
            timeout=REQUEST_TIMEOUT,
        )
        if resp.status_code not in (200, 201):
            return format_http_error(
                "Home Assistant", status_code=resp.status_code, connect_cmd="homeassistant"
            )

        return f"Automation created: {alias} (id: {automation_id})"

    except httpx.HTTPError as e:
        return format_http_error("Home Assistant", exception=e, connect_cmd="homeassistant")


# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------

TOOLS = [
    {
        "name": "ha_list_entities",
        "description": (
            "List all Home Assistant entities, optionally filtered by domain. "
            "Use this to discover available devices and their current states. "
            "Common domains: light, switch, sensor, climate, cover, fan. "
            "Returns up to 50 entities with friendly names and states."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "domain": {
                    "type": "string",
                    "description": "Entity domain to filter by (e.g. light, switch, sensor, climate, cover)",
                },
            },
        },
        "handler": ha_list_entities,
        "category": "homeassistant",
    },
    {
        "name": "ha_get_state",
        "description": (
            "Get the current state and all attributes of a specific Home Assistant entity. "
            "Use this to check the detailed status of a single device or sensor. "
            "Returns the entity state, friendly name, and all attribute key-value pairs."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "entity_id": {
                    "type": "string",
                    "description": "Entity ID (e.g. light.living_room, sensor.temperature)",
                },
            },
            "required": ["entity_id"],
        },
        "handler": ha_get_state,
        "category": "homeassistant",
    },
    {
        "name": "ha_call_service",
        "description": (
            "Call a Home Assistant service to control a device (e.g. light/turn_on, switch/turn_off, "
            "lock/lock, climate/set_temperature, cover/open_cover). "
            "Use this for direct device control when ha_toggle_entity is insufficient. "
            "Requires user confirmation before executing because it changes device state."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "domain": {
                    "type": "string",
                    "description": "Service domain (e.g. light, switch, lock, climate)",
                },
                "service": {
                    "type": "string",
                    "description": "Service name (e.g. turn_on, turn_off, lock, set_temperature)",
                },
                "entity_id": {
                    "type": "string",
                    "description": "Target entity ID",
                },
            },
            "required": ["domain", "service"],
        },
        "handler": ha_call_service,
        "category": "homeassistant",
        "confirm": True,
        "confirm_message": "Call Home Assistant service? This will change the state of a device.",
        "risk_level": "medium",
    },
    {
        "name": "ha_toggle_entity",
        "description": (
            "Toggle a Home Assistant entity between on and off states. "
            "Use this for simple on/off control of lights, switches, fans, and similar devices. "
            "Requires user confirmation before executing because it changes device state."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "entity_id": {
                    "type": "string",
                    "description": "Entity ID to toggle (e.g. light.living_room, switch.fan)",
                },
            },
            "required": ["entity_id"],
        },
        "handler": ha_toggle_entity,
        "category": "homeassistant",
        "confirm": True,
        "confirm_message": "Toggle this entity? This will change its current on/off state.",
        "risk_level": "medium",
    },
    {
        "name": "ha_get_history",
        "description": (
            "Get the state history for a Home Assistant entity over a time period. "
            "Use this to see how a sensor or device state changed over time. "
            "Defaults to the last 24 hours if no timestamp is provided. "
            "Returns up to 20 history entries with timestamps and states."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "entity_id": {
                    "type": "string",
                    "description": "Entity ID to get history for",
                },
                "timestamp": {
                    "type": "string",
                    "description": "Start timestamp in ISO format (e.g. 2026-01-01T00:00:00). Defaults to last 24 hours.",
                },
            },
            "required": ["entity_id"],
        },
        "handler": ha_get_history,
        "category": "homeassistant",
    },
    {
        "name": "ha_create_automation",
        "description": (
            "Create a new Home Assistant automation with triggers, conditions, and actions. "
            "Use this when the user wants to set up automated behaviors for their smart home. "
            "Requires a unique ID, alias, at least one trigger and one action. "
            "Requires user confirmation before executing because it adds a persistent automation rule."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "string",
                    "description": "Unique automation ID (e.g. morning_lights)",
                },
                "alias": {
                    "type": "string",
                    "description": "Friendly name for the automation",
                },
                "description": {
                    "type": "string",
                    "description": "Description of what the automation does",
                },
                "trigger": {
                    "type": "array",
                    "items": {"type": "object"},
                    "description": "List of trigger configurations",
                },
                "condition": {
                    "type": "array",
                    "items": {"type": "object"},
                    "description": "List of condition configurations (optional)",
                },
                "action": {
                    "type": "array",
                    "items": {"type": "object"},
                    "description": "List of action configurations",
                },
                "mode": {
                    "type": "string",
                    "description": "Automation mode (single, restart, queued, parallel)",
                    "default": "single",
                },
            },
            "required": ["id", "alias", "trigger", "action"],
        },
        "handler": ha_create_automation,
        "category": "homeassistant",
        "confirm": True,
        "confirm_message": "Create a new Home Assistant automation? This will add an automation rule to your system.",
        "risk_level": "high",
    },
]
