"""Load, save, and resolve bug bounty program definitions."""

from pathlib import Path

import yaml
from pydantic import ValidationError

import noisegate.config as config
from noisegate.exceptions import ProgramNotFoundError, ProgramParseError
from noisegate.models import Program


def resolve_program(name_or_path: str) -> Path:
    """Return a Path for the given name or explicit path.

    'acme' → ~/.config/noisegate/programs/acme.yaml
    Explicit paths (absolute or containing a path separator) pass through as-is.
    """
    p = Path(name_or_path)
    if p.is_absolute() or "/" in name_or_path or "\\" in name_or_path:
        return p
    return config._PROGRAMS_DIR / f"{name_or_path}.yaml"


def load_program(path: Path) -> Program:
    """Parse a YAML program file into a Program model.

    Raises ProgramParseError on bad YAML or validation failure.
    """
    try:
        raw = yaml.safe_load(path.read_text())
    except (yaml.YAMLError, OSError) as exc:
        raise ProgramParseError(path, str(exc)) from exc

    try:
        return Program.model_validate(raw)
    except ValidationError as exc:
        raise ProgramParseError(path, str(exc)) from exc


def save_program(program: Program, path: Path) -> None:
    """Serialize a Program to YAML and write it to path (creates parent dirs)."""
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(yaml.dump(program.model_dump(), sort_keys=False, allow_unicode=True))


def list_programs() -> list[str]:
    """Return names of all saved programs (alphabetical)."""
    if not config._PROGRAMS_DIR.exists():
        return []
    return sorted(p.stem for p in config._PROGRAMS_DIR.glob("*.yaml"))


def remove_program(name: str) -> None:
    """Delete a named program file. Raises ProgramNotFoundError if it doesn't exist."""
    path = config._PROGRAMS_DIR / f"{name}.yaml"
    if not path.exists():
        raise ProgramNotFoundError(name)
    path.unlink()
    cfg = config._read_config()
    if cfg.get("default_program") == name:
        del cfg["default_program"]
        config._write_config(cfg)
