from __future__ import annotations

import os
from pathlib import Path

import yaml
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings

_CONFIG_YAML = Path("/config/ha-diag-agent.yaml")

# node_name values that are unsafe to run with. A node_name of "unknown" (or
# empty) silently produces evt-unknown-* event files. Such a file once landed in
# events/piha/ and, being lexically greater than every evt-piha-* name, pinned
# the observer's (then lexical) checkpoint and blocked PIHA for ~34 days — see
# fix d5139c9. We refuse to start rather than ever emit that poison again.
_FORBIDDEN_NODE_NAMES = {"", "unknown"}


class Settings(BaseSettings):
    # HA connection
    ha_url: str = "http://homeassistant.local:8123"
    ha_token: str = ""
    ha_timeout: float = 10.0

    # Node identity.
    # validate_default=True so the "unknown" default itself is rejected: if
    # NODE_NAME never reaches the process the agent must fail fast, not run as
    # "unknown" and poison another node's event directory (see d5139c9).
    node_name: str = Field(default="unknown", validate_default=True)
    location_tag: str = "default"

    # Intervals (seconds)
    check_interval: int = 60          # heartbeat check interval
    check_interval_unavailable: int = 3600  # unavailable entities check interval

    # Unavailable entities check thresholds
    unavailable_threshold_hours: float = 24.0  # alert after N hours unavailable
    integration_failure_threshold_pct: float = 0.5  # % of integration entities unavailable
    integration_failure_min_entities: int = 3   # min count to trigger integration event
    alert_cooldown_hours: float = 6.0           # don't re-alert same entity within N hours

    # Phase 3 Flag #3: entity registry cache TTL
    entity_registry_cache_ttl: int = 300  # seconds

    # SystemHealthCheck
    system_health_check_interval: int = 900   # 15 min

    # AutomationFailuresCheck
    automation_check_interval: int = 1800     # 30 min
    automation_failure_threshold: int = 3     # consecutive failures before alert

    # UpdatesAvailableCheck
    updates_check_hour: int = 9
    updates_check_minute: int = 0
    updates_cooldown_days: int = 7            # don't re-alert same update within N days

    # WebSocket monitor
    websocket_enabled: bool = True
    websocket_silence_threshold_seconds: int = 300   # 5 min
    websocket_watchdog_interval_seconds: int = 30
    websocket_reconnect_initial_delay: float = 1.0
    websocket_reconnect_max_delay: float = 60.0
    websocket_reconnect_jitter: float = 0.2          # ±20% of delay
    websocket_down_alert_repeat_minutes: int = 10

    # API server
    port: int = 8087
    log_level: str = "info"

    # Runtime paths (inside container)
    events_dir: Path = Path("/events")
    data_dir: Path = Path("/data")

    model_config = {"extra": "ignore", "case_sensitive": False}

    @field_validator("ha_url")
    @classmethod
    def strip_trailing_slash(cls, v: str) -> str:
        return v.rstrip("/")

    @field_validator("node_name")
    @classmethod
    def reject_unknown_node(cls, v: str) -> str:
        """Fail-fast: never let the agent run as "unknown"/empty.

        A node_name of "unknown" makes EventEmitter write evt-unknown-* files.
        Landing in another node's events dir, such a file poisoned the observer
        checkpoint and blocked PIHA for ~34 days (d5139c9). Refusing to start is
        strictly safer than silently emitting the poison.
        """
        normalized = (v or "").strip()
        if normalized.lower() in _FORBIDDEN_NODE_NAMES:
            raise ValueError(
                "NODE_NAME not set — refusing to start, would poison event "
                "pipeline with evt-unknown-* files (see d5139c9). "
                "Set NODE_NAME to the node's canonical inventory name "
                "(e.g. piha, chelsty-infra)."
            )
        return normalized

    @classmethod
    def load(cls) -> "Settings":
        """Load settings: YAML file provides defaults; env vars override."""
        if _CONFIG_YAML.exists():
            try:
                with _CONFIG_YAML.open() as f:
                    data = yaml.safe_load(f) or {}
                for k, v in data.items():
                    os.environ.setdefault(k.upper(), str(v))
            except Exception:
                pass
        return cls()
