"""Build management: invoke pmq-core binary with freshness tracking.

pmq-core is the Rust XML parser, bundled in the wheel by maturin.
It is installed to the venv's bin/ directory alongside python.
"""
import shutil
import subprocess
import sys
from contextlib import contextmanager
from datetime import datetime, timezone
from pathlib import Path
from platformdirs import user_data_dir

try:
    import fcntl
    _UNIX = True
except ImportError:
    import msvcrt
    import time as _time
    _UNIX = False

BUILD_FRESH_SECS = 30 * 60


@contextmanager
def flock(path):
    """Cross-process file lock. fcntl on Unix, msvcrt on Windows."""
    Path(path).parent.mkdir(parents=True, exist_ok=True)
    f = open(path, 'w')
    try:
        if _UNIX:
            fcntl.flock(f, fcntl.LOCK_EX)
        else:
            # msvcrt.LK_NBLCK + retry: LK_LOCK only waits 10s then fails
            while True:
                try:
                    msvcrt.locking(f.fileno(), msvcrt.LK_NBLCK, 1)
                    break
                except OSError:
                    _time.sleep(5)
        yield
    finally:
        if _UNIX:
            fcntl.flock(f, fcntl.LOCK_UN)
        else:
            try:
                f.seek(0)
                msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, 1)
            except OSError:
                pass
        f.close()


def default_data_dir() -> Path:
    return Path(user_data_dir('pmq'))


def is_fresh(data_dir: Path) -> bool:
    p = data_dir / '.latest-build-date'
    if not p.exists():
        return False
    try:
        last = datetime.fromisoformat(p.read_text().strip())
        return (datetime.now(timezone.utc) - last).total_seconds() < BUILD_FRESH_SECS
    except (ValueError, OSError):
        return False


def write_build_date(data_dir: Path):
    data_dir.mkdir(parents=True, exist_ok=True)
    (data_dir / '.latest-build-date').write_text(
        datetime.now(timezone.utc).isoformat())


def _find_binary() -> str:
    """Find pmq-core binary.
    Search order: PATH, venv bin/, cargo target/release/."""
    path = shutil.which("pmq-core")
    if path:
        return path
    # Venv bin dir (maturin wheel install)
    bin_dir = Path(sys.executable).parent
    for name in ("pmq-core", "pmq-core.exe"):
        candidate = bin_dir / name
        if candidate.is_file():
            return str(candidate)
    # Cargo build output (development)
    for root in (Path.cwd(), Path(__file__).resolve().parent.parent.parent):
        candidate = root / "target" / "release" / "pmq-core"
        if candidate.is_file():
            return str(candidate)
    raise RuntimeError(
        "pmq-core not found.\n"
        "Development: cargo build --release\n"
        "Install:     uv add pmq"
    )


def ensure_build(data_dir: Path, extra_args: list[str] | None = None):
    """Run pmq-core build if stale or absent. Process-safe, idempotent."""
    with flock(data_dir / '.lock-build'):
        if is_fresh(data_dir):
            return
        binary = _find_binary()
        data_dir.mkdir(parents=True, exist_ok=True)
        cmd = [binary, "build", "--out-dir", str(data_dir), "--no-check"]
        if extra_args:
            cmd.extend(extra_args)
        subprocess.run(cmd, check=True)
        write_build_date(data_dir)
