"""Smart home control use case built on top of the OmniLink Python engine."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any, Callable, Dict

from omnilink import OmniLinkEngine, TypeRegistry, load_patterns_from_file

from omnilink.use_case_loader import UseCaseConfig
from .api import (
    get_light_status,
    get_summary,
    get_thermostat_state,
    lock_door,
    set_thermostat_mode,
    set_thermostat_temperature,
    turn_off_light,
    turn_on_light,
    unlock_door,
)

HERE = Path(__file__).resolve().parent
PATTERNS_FILE = HERE / "commands.txt"

Handler = Callable[[Dict[str, Any]], Dict[str, Any]]


def _call_with_key(
    evt: Dict[str, Any],
    *,
    key: str,
    handler: Callable[[str], Dict[str, Any]],
) -> Dict[str, Any]:
    value = evt.get("vars", {}).get(key)
    if not value:
        return {"ack": False, "error": f"missing {key}"}
    try:
        result = handler(value)
    except Exception as exc:  # pragma: no cover - defensive logging
        return {"ack": False, "error": str(exc)}
    return {"ack": True, "result": result}


def _call_with_value(
    evt: Dict[str, Any],
    *,
    key: str,
    handler: Callable[[Any], Dict[str, Any]],
) -> Dict[str, Any]:
    vars_ = evt.get("vars", {})
    if key not in vars_:
        return {"ack": False, "error": f"missing {key}"}
    try:
        result = handler(vars_[key])
    except Exception as exc:  # pragma: no cover - defensive logging
        return {"ack": False, "error": str(exc)}
    return {"ack": True, "result": result}


def _handle_summary(_evt: Dict[str, Any]) -> Dict[str, Any]:
    try:
        return {"ack": True, "result": get_summary()}
    except Exception as exc:  # pragma: no cover - defensive logging
        return {"ack": False, "error": str(exc)}


def _handle_thermostat_status(_evt: Dict[str, Any]) -> Dict[str, Any]:
    try:
        return {"ack": True, "result": get_thermostat_state()}
    except Exception as exc:  # pragma: no cover - defensive logging
        return {"ack": False, "error": str(exc)}


_DISPATCH: Dict[str, Handler] = {
    "turn_on_the_[room:room]_lights": lambda evt: _call_with_key(
        evt, key="room", handler=turn_on_light
    ),
    "turn_off_the_[room:room]_lights": lambda evt: _call_with_key(
        evt, key="room", handler=turn_off_light
    ),
    "what_is_the_status_of_the_[room:room]_lights": lambda evt: _call_with_key(
        evt, key="room", handler=get_light_status
    ),
    "set_the_thermostat_to_[temperature:float]_degrees": lambda evt: _call_with_value(
        evt, key="temperature", handler=set_thermostat_temperature
    ),
    "set_the_thermostat_mode_to_[mode:mode]": lambda evt: _call_with_value(
        evt, key="mode", handler=set_thermostat_mode
    ),
    "what_is_the_thermostat_setting": _handle_thermostat_status,
    "lock_the_[door:door]": lambda evt: _call_with_key(
        evt, key="door", handler=lock_door
    ),
    "unlock_the_[door:door]": lambda evt: _call_with_key(
        evt, key="door", handler=unlock_door
    ),
    "show_me_the_smart_home_summary": _handle_summary,
}


def _configure(engine: OmniLinkEngine) -> None:
    engine.on(lambda _evt: True, _handle_event)


def _handle_event(evt: Dict[str, Any]) -> Dict[str, Any]:
    template = evt.get("template")
    if not template:
        return {"ack": False, "error": "no_template"}
    handler = _DISPATCH.get(template)
    if handler is None:
        return {"ack": False, "error": f"unsupported_template:{template}"}
    return handler(evt)


def _build_context_payload() -> str:
    try:
        return json.dumps(get_summary())
    except Exception as exc:  # pragma: no cover - telemetry helper
        return json.dumps({"error": str(exc)})


def build_use_case() -> UseCaseConfig:
    types = TypeRegistry()
    types.register("room", r"(?:living_room|kitchen|bedroom|office)")
    types.register("door", r"(?:front_door|garage_door|back_door)")
    types.register("mode", r"(?:heat|cool|auto|off)")

    templates = load_patterns_from_file(PATTERNS_FILE, types)
    return UseCaseConfig(
        types=types,
        templates=templates,
        configure=_configure,
        context_provider=_build_context_payload,
    )


USE_CASE = build_use_case()


__all__ = [
    "PATTERNS_FILE",
    "USE_CASE",
    "build_use_case",
]
