"""Job definition YAML loader and saver."""

from pathlib import Path

import structlog
import yaml

from fliiq.runtime.scheduler.models import JobDefinition, JobState

log = structlog.get_logger()


def load_job(path: Path) -> JobDefinition:
    """Load a single job definition from a YAML file."""
    raw = yaml.safe_load(path.read_text(encoding="utf-8"))
    if not isinstance(raw, dict):
        raise ValueError(f"Invalid job file (expected mapping): {path}")

    # Map _state -> state for Pydantic
    if "_state" in raw:
        raw["state"] = raw.pop("_state")

    return JobDefinition(**raw)


def load_jobs(jobs_dir: Path) -> list[JobDefinition]:
    """Load all job definitions from a directory. Skips invalid files with a warning."""
    if not jobs_dir.is_dir():
        return []

    jobs: list[JobDefinition] = []
    for path in sorted(jobs_dir.glob("*.yaml")):
        try:
            jobs.append(load_job(path))
        except Exception as e:
            log.warning("skip_invalid_job", path=str(path), error=str(e))
    return jobs


def save_job(job: JobDefinition, jobs_dir: Path) -> Path:
    """Save a job definition to YAML. Returns the file path."""
    jobs_dir.mkdir(parents=True, exist_ok=True)
    path = jobs_dir / f"{job.name}.yaml"

    data = job.model_dump(mode="json", exclude_none=True)

    # Map state -> _state for YAML (only include if non-default)
    if "state" in data:
        state = data.pop("state")
        has_values = any(v is not None for v in state.values() if not isinstance(v, int))
        if state and (has_values or state.get("run_count", 0) > 0):
            data["_state"] = state

    path.write_text(yaml.dump(data, default_flow_style=False, sort_keys=False), encoding="utf-8")
    return path


def delete_job(name: str, jobs_dir: Path) -> bool:
    """Delete a job YAML file. Returns True if file existed."""
    path = jobs_dir / f"{name}.yaml"
    if path.is_file():
        path.unlink()
        return True
    return False


def update_job_state(name: str, jobs_dir: Path, state: JobState) -> None:
    """Update only the _state section of a job file. Preserves all other fields."""
    path = jobs_dir / f"{name}.yaml"
    if not path.is_file():
        raise FileNotFoundError(f"Job file not found: {path}")

    job = load_job(path)
    job.state = state
    save_job(job, jobs_dir)
