from __future__ import annotations

from pathlib import Path

import yaml

from one_run.errors import OneRunError
from one_run.models import ListenerConfig
from one_run.listeners.routes import ListenerRoute, route_from_listener


def _load_manifest_payload(manifest_path: str | None) -> dict:
    if not manifest_path:
        return {}
    path = Path(manifest_path)
    if not path.exists():
        raise OneRunError(f"manifest not found: {path}")
    try:
        payload = yaml.safe_load(path.read_text(encoding="utf-8"))
    except yaml.YAMLError as exc:
        raise OneRunError(f"invalid yaml in {path}: {exc}") from exc
    if not isinstance(payload, dict):
        raise OneRunError(f"invalid listener manifest: expected mapping in {path}")
    return payload


def build_routes_from_manifest(manifest_path: str | None) -> list[ListenerRoute]:
    payload = _load_manifest_payload(manifest_path)
    if not payload:
        return []
    raw_listeners = payload.get("listeners")
    if raw_listeners is None:
        raise OneRunError(f"listeners section not found in {manifest_path}")
    if not isinstance(raw_listeners, list):
        raise OneRunError(f"listeners must be a list in {manifest_path}")
    listeners = [ListenerConfig.model_validate(item) for item in raw_listeners]
    return [route_from_listener(listener, idx=idx) for idx, listener in enumerate(listeners)]


def manifest_interval_sec(manifest_path: str | None) -> float | None:
    payload = _load_manifest_payload(manifest_path)
    if not payload:
        return None
    raw = payload.get("interval_sec")
    if raw is None:
        return None
    try:
        value = float(raw)
    except (TypeError, ValueError) as exc:
        raise OneRunError(f"interval_sec must be a positive number in {manifest_path}") from exc
    if value <= 0:
        raise OneRunError(f"interval_sec must be > 0 in {manifest_path}")
    return value
