#!/usr/bin/env python3
"""T^GPT v0.8.1 hostile baseline: greedy budgeted semantic coverage.

Input JSON schema:
{
  "token_budget": 2000,
  "clusters": [{"id":"C1","weight":0.4}, ...],
  "candidates": [
    {"id":"S1","token_cost":120,"covers":{"C1":1.0,"C2":0.25}},
    ...
  ]
}

Objective:
F(A) = sum_c w(c) * max_{i in A} cover(i,c)
subject to sum_i token_cost(i) <= token_budget.

Greedy chooses maximal marginal-gain/token-cost ratio with deterministic
tie-breaking. This is a transparent hostile baseline, not a claim of global
optimality for budgeted maximum coverage.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Dict, List, Tuple

EPS = 1e-12


def load_spec(path: Path) -> dict:
    data = json.loads(path.read_text(encoding="utf-8"))
    if int(data.get("token_budget", 0)) <= 0:
        raise ValueError("token_budget must be > 0")
    return data


def objective(selected: List[dict], weights: Dict[str, float]) -> Tuple[float, Dict[str, float]]:
    best = {c: 0.0 for c in weights}
    for item in selected:
        for c, score in item.get("covers", {}).items():
            if c in best:
                best[c] = max(best[c], max(0.0, min(1.0, float(score))))
    value = sum(weights[c] * best[c] for c in weights)
    return value, best


def greedy_select(data: dict) -> dict:
    budget = int(data["token_budget"])
    weights = {str(x["id"]): float(x.get("weight", 1.0)) for x in data["clusters"]}
    candidates = []
    for raw in data["candidates"]:
        item = dict(raw)
        item["id"] = str(item["id"])
        item["token_cost"] = int(item["token_cost"])
        if item["token_cost"] <= 0:
            raise ValueError(f"token_cost must be >0 for {item['id']}")
        candidates.append(item)

    selected: List[dict] = []
    remaining = {x["id"]: x for x in candidates}
    spent = 0
    current_value, _ = objective(selected, weights)
    trace = []

    while remaining:
        feasible = [x for x in remaining.values() if spent + x["token_cost"] <= budget]
        if not feasible:
            break

        scored = []
        for item in feasible:
            candidate_value, _ = objective(selected + [item], weights)
            gain = candidate_value - current_value
            ratio = gain / item["token_cost"]
            scored.append((ratio, gain, -item["token_cost"], item["id"], item, candidate_value))

        scored.sort(key=lambda t: (-t[0], -t[1], -t[2], t[3]))
        ratio, gain, _, _, chosen, new_value = scored[0]
        if gain <= EPS:
            break

        selected.append(chosen)
        spent += chosen["token_cost"]
        remaining.pop(chosen["id"], None)
        trace.append({
            "candidate_id": chosen["id"],
            "marginal_gain": gain,
            "gain_per_token": ratio,
            "spent_tokens": spent,
            "objective_after": new_value,
        })
        current_value = new_value

    final_value, per_cluster = objective(selected, weights)
    total_weight = sum(weights.values())
    normalized = final_value / total_weight if total_weight > 0 else None
    return {
        "schema": "T_GPT_COVERAGE_BASELINE_RESULT_V0_8_1",
        "algorithm": "deterministic_greedy_budgeted_weighted_max_coverage",
        "token_budget": budget,
        "tokens_spent": spent,
        "selected_ids": [x["id"] for x in selected],
        "objective_weighted": final_value,
        "objective_normalized": normalized,
        "cluster_coverage": per_cluster,
        "trace": trace,
        "note": "Hostile baseline; transparent and reproducible, not a claim of global optimality."
    }


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("input", type=Path)
    ap.add_argument("-o", "--output", type=Path)
    args = ap.parse_args()
    result = greedy_select(load_spec(args.input))
    payload = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
    if args.output:
        args.output.write_text(payload, encoding="utf-8")
    else:
        print(payload, end="")


if __name__ == "__main__":
    main()
