"""Codex state-db backfill coordination.

A fresh CODEX_HOME rebuilds its state database before `codex app-server` answers
anything. The worker marks `backfill_state.status = 'running'` and codex gives up
after 30s when that mark is held by nobody, so a probe that kills the worker
leaves the account permanently unanswerable.
"""

from __future__ import annotations

import contextlib
import os
import re
import sqlite3
import time
from pathlib import Path
from typing import Iterator

WARMUP_TIMEOUT_S = 1800.0

_MARKER_NAME = ".systray-backfill-worker"
_PROC = "/proc"
_STATE_DB_GLOB = "state_*.sqlite"
_STATE_DB_VERSION = re.compile(r"state_(\d+)\.sqlite$")
_COMPLETE = "complete"
_RUNNING = "running"


def state_db_path(codex_home: Path) -> Path | None:
    try:
        candidates = [path for path in codex_home.glob(_STATE_DB_GLOB) if path.is_file()]
    except OSError:
        return None
    if not candidates:
        return None
    return max(candidates, key=_state_db_version)


def needs_warmup(codex_home: Path) -> bool:
    """True while an existing state db reports an unfinished backfill."""
    db_path = state_db_path(codex_home)
    if db_path is None:
        return False
    status = _read_status(db_path)
    return status is not None and status != _COMPLETE


def warmup_in_progress(codex_home: Path) -> bool:
    return _worker_is_live(_marker_path(codex_home))


def release_orphaned_claim(codex_home: Path) -> bool:
    db_path = state_db_path(codex_home)
    if db_path is None or _worker_is_live(_marker_path(codex_home)):
        return False
    if _read_status(db_path) != _RUNNING:
        return False
    if _state_db_is_open(db_path):
        return False
    try:
        connection = sqlite3.connect(db_path, timeout=5.0)
    except sqlite3.Error:
        return False
    try:
        with connection:
            connection.execute(
                "UPDATE backfill_state SET status = ?, updated_at = ? WHERE id = 1",
                ("pending", int(time.time())),
            )
    except sqlite3.Error:
        return False
    finally:
        connection.close()
    return True


@contextlib.contextmanager
def warmup_claim(codex_home: Path) -> Iterator[None]:
    marker = _marker_path(codex_home)
    try:
        marker.write_text(f"{os.getpid()}\n", encoding="utf-8")
    except OSError:
        yield
        return
    try:
        yield
    finally:
        with contextlib.suppress(OSError):
            marker.unlink()


def _state_db_version(path: Path) -> int:
    match = _STATE_DB_VERSION.search(path.name)
    return int(match.group(1)) if match else -1


def _read_status(db_path: Path) -> str | None:
    try:
        connection = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True, timeout=1.0)
    except sqlite3.Error:
        return None
    try:
        row = connection.execute("SELECT status FROM backfill_state WHERE id = 1").fetchone()
    except sqlite3.Error:
        return None
    finally:
        connection.close()
    if row is None or not isinstance(row[0], str):
        return None
    return row[0]


def _state_db_is_open(db_path: Path) -> bool:
    """A worker outside this process leaves no marker, but it does hold the db open."""
    try:
        target = db_path.resolve()
    except OSError:
        return False
    own_pid = str(os.getpid())
    try:
        pids = [entry for entry in os.listdir(_PROC) if entry.isdigit() and entry != own_pid]
    except OSError:
        return False
    for pid in pids:
        fd_dir = Path(_PROC) / pid / "fd"
        try:
            entries = list(fd_dir.iterdir())
        except OSError:
            continue
        for entry in entries:
            try:
                if entry.resolve() == target:
                    return True
            except OSError:
                continue
    return False


def _marker_path(codex_home: Path) -> Path:
    return codex_home / _MARKER_NAME


def _worker_is_live(marker: Path) -> bool:
    try:
        pid = int(marker.read_text(encoding="utf-8").strip())
        age = time.time() - marker.stat().st_mtime
    except (OSError, ValueError):
        return False
    if age > WARMUP_TIMEOUT_S:
        return False
    if pid == os.getpid():
        return True
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    return True
