"""Worker status reading, task scheduling, and container polling for orchestrate execute."""

from __future__ import annotations

import json
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Any

from vibepod.core.orchestrate.plan_file import OrchestratePlan, OrchestrateTask


@dataclass
class StatusResult:
    status: str          # "done" | "failed"
    summary: str = ""
    reason: str = ""
    files_changed: list[str] | None = None


def read_status_file(path: Path) -> StatusResult | None:
    """Read and validate a worker status JSON file.

    Returns None if the file is missing, unreadable, or malformed.
    """
    if not path.exists():
        return None
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return None

    status = data.get("status")
    if status not in ("done", "failed"):
        return None

    return StatusResult(
        status=status,
        summary=str(data.get("summary", "")),
        reason=str(data.get("reason", "")),
        files_changed=data.get("files_changed"),
    )


def all_deps_done(task: OrchestrateTask, plan: OrchestratePlan) -> bool:
    """Return True when all dependencies of task are in 'done' status."""
    status_by_id = {t.id: t.status for t in plan.tasks}
    return all(status_by_id.get(dep) == "done" for dep in task.depends_on)


def any_dep_failed(task: OrchestrateTask, plan: OrchestratePlan) -> bool:
    """Return True when any dependency of task is in 'failed' or 'blocked' status."""
    status_by_id = {t.id: t.status for t in plan.tasks}
    return any(
        status_by_id.get(dep) in ("failed", "blocked") for dep in task.depends_on
    )


def tasks_ready_to_run(plan: OrchestratePlan) -> list[OrchestrateTask]:
    """Return tasks that are eligible to start (deps met, not already running/done)."""
    ready = []
    for task in plan.tasks:
        if task.status not in ("pending", "waiting"):
            continue
        if any_dep_failed(task, plan):
            continue
        if task.depends_on and not all_deps_done(task, plan):
            continue
        ready.append(task)
    return ready


def poll_worker(
    status_file: Path,
    container: Any,
    poll_interval: float = 5.0,
    timeout: float = 3600.0,
) -> StatusResult | None:
    """Poll until the worker writes a status file or the container exits.

    Returns a StatusResult, or None if timed out without a result.
    poll_interval and timeout are in seconds.
    """
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        result = read_status_file(status_file)
        if result is not None:
            return result

        # Check if container is still running
        try:
            container.reload()
            if container.status not in ("running", "created"):
                # Container stopped without writing status — treat as failed
                result = read_status_file(status_file)
                if result is not None:
                    return result
                return StatusResult(status="failed", reason="container exited without status file")
        except Exception:
            return StatusResult(status="failed", reason="container unreachable")

        time.sleep(poll_interval)

    return None  # timed out
