"""Full-catalog batch runner — SKELETON (Fable 5, 2026-07-07).

Sonnet: implement the TODO functions, do not redesign the flow. The staging
logic, resume mechanics and budget enforcement are final; see
docs/batch-architecture.md for the design and data contracts.

Stages (per skillfactor_finalize.md / auftrag_sonnet.md):
  1. packages:  deterministic generator for ALL ESCO occupations (no LLM)
  2. evidence:  per occupation fetch (JSearch, budget-capped) -> extract
                (Ollama, extract_local.py) -> aggregate (market.md)
  3. depth:     tier 1 = top-200 by evidence volume, tier 3 = night batch

Every step is idempotent and resumable: progress.json + file existence
decide what still needs work; an abort at any point loses at most the
current occupation's in-flight step.

Usage:
  python pipeline/batch_run.py --stage packages [--limit N]
  python pipeline/batch_run.py --stage evidence [--slugs a,b] [--limit N]
  python pipeline/batch_run.py --stage tier    # (re)compute tiers only
"""
import argparse
import os
import subprocess
import sys

sys.path.insert(0, os.path.dirname(__file__))
import progress
from db import connect

BASE = os.path.join(os.path.dirname(__file__), "..")
ADS_PER_OCC_TARGET = 60      # ~6 requests/occupation (1 req ~ 10 ads)
COUNTRIES = ("us", "gb")
REQ_RATE_SLEEP = 0.5         # ~2 req/s max (finalize order)
TOP_TIER_SIZE = 200


def esco_occupations(cur):
    """All ESCO occupations with slug + altLabels for query building."""
    # TODO(sonnet): return [(slug, preferred_label, [alt_labels...]), ...]
    # from esco_occupation; slug generation must match p2_generate.py
    raise NotImplementedError


def stage_packages(state, limit=0):
    """Run the existing generator for every occupation without a package.
    TODO(sonnet): refactor p2_generate.py so it can be called per-occupation
    (it currently generates a fixed balanced set of 100). Reuse, don't fork.
    Gitea pushes go through p4_publish.py, throttled (sleep >= 1s/repo,
    resumable by checking repo existence via API before create)."""
    raise NotImplementedError


def stage_evidence(state, slugs=None, limit=0):
    """Per occupation: fetch (cached) -> extract (Ollama) -> aggregate.

    fetch:   generalize p3a (queries = preferredLabel + top-3 altLabels x
             COUNTRIES, cache data/raw/jobs/<slug>/, budget via
             progress.spend_request BEFORE each HTTP call).
             < 20 usable ads -> synonym fallback, then Adzuna (free), then
             mark evidence="failed", error="thin market" and move on.
    extract: subprocess extract_local.py --ads data/raw/jobs/<slug>_ads.json
             --out data/evidence/<slug>.jsonl  (already implemented + tested)
    store:   extend p3b: incremental MERGE keyed by (occupation_slug, job_id)
             -- needs ALTER TABLE evidence_job ADD occupation_slug (see
             docs/batch-architecture.md, backfill 'recruitment-consultant')
    aggregate: generalize p3c per occupation (market.md + market-evidence
             sections); QA: run qa_sample.py per batch, stop the run if the
             harness exits 1 twice in a row (systemic problem, not noise).
    """
    raise NotImplementedError


def stage_tier(state):
    """Rank occupations for depth staging: tier 1 = TOP_TIER_SIZE by
    (ads collected, crosswalk strength, marketplace demand), rest tier 3.
    Pure bookkeeping in progress.json — no API calls."""
    raise NotImplementedError


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--stage", required=True,
                    choices=["packages", "evidence", "tier"])
    ap.add_argument("--slugs", default="")
    ap.add_argument("--limit", type=int, default=0)
    args = ap.parse_args()

    state = progress.load()
    try:
        if args.stage == "packages":
            stage_packages(state, args.limit)
        elif args.stage == "evidence":
            slugs = [s for s in args.slugs.split(",") if s] or None
            stage_evidence(state, slugs, args.limit)
        else:
            stage_tier(state)
    except progress.BudgetExhausted as exc:
        print(f"STOP: {exc}")
        sys.exit(2)
    finally:
        progress.save(state)


if __name__ == "__main__":
    main()
