from __future__ import annotations

import time
from collections import deque
from datetime import datetime
from pathlib import Path

from one_run.utils.duration import parse_duration
from one_run.constants import DEFAULT_RUNS_DIR
from one_run.errors import OneRunError


def _log_path(run_id: str, project_root: Path, stderr: bool) -> Path:
    run_dir = project_root / DEFAULT_RUNS_DIR / run_id
    if not run_dir.exists():
        raise OneRunError(f"run not found: {run_id}")
    path = run_dir / "logs" / ("stderr.log" if stderr else "stdout.log")
    if not path.exists():
        raise OneRunError(f"log file not found: {path}")
    return path


def _print_last_lines(path: Path, n: int) -> None:
    with path.open("r", encoding="utf-8", errors="replace") as f:
        lines = deque(f, maxlen=n)
    for line in lines:
        print(line, end="")


def _follow(path: Path, poll_sec: float = 0.5) -> None:
    with path.open("r", encoding="utf-8", errors="replace") as f:
        f.seek(0, 2)
        while True:
            line = f.readline()
            if line:
                print(line, end="")
                continue
            time.sleep(poll_sec)


def tail_run_log(
    run_id: str,
    *,
    stderr: bool = False,
    follow: bool = False,
    lines: int = 30,
    since: str | None = None,
) -> int:
    project_root = Path.cwd().resolve()
    path = _log_path(run_id, project_root, stderr)

    if since:
        try:
            cutoff = datetime.now().timestamp() - parse_duration(since).total_seconds()
        except ValueError as exc:
            raise OneRunError(str(exc)) from exc

        if path.stat().st_mtime >= cutoff:
            _print_last_lines(path, lines)
    else:
        _print_last_lines(path, lines)

    if follow:
        try:
            _follow(path)
        except KeyboardInterrupt:
            return 0
    return 0
