"""Homepage category awards (published at /how-we-rank/#awards). Same public rules for all providers in inputs.json.
award score = 0.75 * award objective + 0.25 * Reputation' (same Reputation' as the main formula); strict order by unrounded score.
Every input is 0-10 and comes from score.input_scores() or the provider's recorded feature flags (true 10 / false 0 / unknown 5)."""
import score
HOURLY = lambda x: 10.0 if x["billing"] == "hourly" else 0.0
def tri(v): return score.tri(v)
AWARDS = [
 {"id": "value", "title": "Best value for most US VPS buyers", "short": "Best value",
  "rule": "2 GB plan billed month to month or hourly (no prepay), orderable in the US. Objective = 70% price of that 2 GB plan (10 x sqrt(cheapest / price) among eligible plans) + 30% renewal transparency and billing flexibility.",
  "gate": lambda x: x["plans"].get("2gb") and x["plans"]["2gb"].get("us_purchasable") and x["plans"]["2gb"]["billing"] in ("monthly", "hourly"),
  "weights": {"price_2gb": .70, "renewal_billing": .30}},
 {"id": "developer", "title": "Best developer cloud (API, hourly billing, automation)", "short": "Best developer cloud",
  "rule": "Objective = one third provider API + one third hourly billing + one third automation (share present of: official CLI, Terraform provider that creates servers, init script / cloud-init user data at create). Yes 10 / no 0 / not verified 5. Nothing else (no regions, sizing or price).",
  "gate": lambda x: True,
  "weights": {"api": 1/3, "hourly": 1/3, "automation": 1/3}},
 {"id": "configurable", "title": "Best flexible configuration", "short": "Best flexible configuration",
  "rule": "Objective = 60% custom sizing (CPU, RAM and disk chosen independently at order: yes 10 / no 0 / not verified 5) + 25% feature coverage (all 10 recorded features) + 15% hourly billing.",
  "gate": lambda x: True,
  "weights": {"custom_sizing": .60, "features": .25, "hourly": .15}},
 {"id": "ecosystem", "title": "Best cloud ecosystem", "short": "Best cloud ecosystem",
  "rule": "Objective = 60% managed services (managed Kubernetes, load balancer, block storage, monitoring, backups; share present) + 20% one-click marketplace + 20% US locations.",
  "gate": lambda x: True,
  "weights": {"managed": .60, "one_click": .20, "us_locations": .20}},
 {"id": "alt_hourly", "title": "Best alternative hourly cloud", "short": "Best alternative hourly cloud",
  "rule": "Hourly-billed providers with a US 2 GB plan. Objective = 50% price of the 2 GB plan (among these) + 25% features + 25% US locations.",
  "gate": lambda x: x["billing"] == "hourly" and x["plans"].get("2gb") and x["plans"]["2gb"].get("us_purchasable"),
  "weights": {"price_2gb": .50, "features": .25, "us_locations": .25}},
]
def compute(P):
    out = []; winners = {}
    for a in AWARDS:
        cand = [c for c in P if a["gate"](P[c])]
        S = score.input_scores(P, cand, "homepage")
        rows = []
        for c in cand:
            x = P[c]; f = x["features"]; s = dict(S[c])
            s["hourly"] = HOURLY(x); s["api"] = tri(f["api"])
            s["dev_features"] = sum(tri(f[k]) for k in ("snapshot", "custom_iso", "block_storage", "firewall")) / 4
            s["managed"] = sum(tri(f[k]) for k in ("managed_k8s", "load_balancer", "block_storage", "monitoring", "backup")) / 5
            s["one_click"] = tri(f["one_click"]); au = x.get("automation", {}); s["automation"] = sum(tri(au.get(k, "unknown")) for k in ("cli", "terraform", "init_script")) / 3; s["custom_sizing"] = tri(x.get("custom_sizing", "unknown"))
            O = sum(w * s[k] for k, w in a["weights"].items())
            _, _, Rc, _ = score.reputation(x)
            rows.append({"id": c, "name": x["name"], "objective": O, "rep": Rc, "total": .75 * O + .25 * Rc, "inputs": {k: round(s[k], 2) for k in a["weights"]}})
        rows.sort(key=lambda r: (-r["total"], -r["rep"]))
        for i, r in enumerate(rows, 1): r["rank"] = i
        out.append({k: a[k] for k in ("id", "title", "short", "rule", "weights")} | {"excluded": [c for c in P if c not in cand], "rows": rows})
    return assign(out)
ASSIGN_RULE = ("Each provider wins at most one award. Awards are resolved by highest winning score first: among all awards not yet resolved, "
  "take the award whose best still-available provider has the highest award score (ties: higher Reputation', then the order awards are listed); "
  "that provider wins it and becomes unavailable; repeat until every award has a winner. A provider that tops an award it cannot take "
  "(because it already won a higher-scoring one) is shown as skipped.")
def assign(out):
    used = set(); left = list(range(len(out))); step = 0
    while left:
        best = None
        for i in left:
            r = next((r for r in out[i]["rows"] if r["id"] not in used), None)
            if r is None: continue
            key = (-r["total"], -r["rep"], i)
            if best is None or key < best[0]: best = (key, i, r)
        if best is None:
            for i in left: out[i]["winner"] = None
            break
        _, i, r = best; step += 1
        out[i]["winner"] = r["id"]; out[i]["winner_row"] = r; out[i]["resolved_step"] = step
        out[i]["skipped"] = [x["id"] for x in out[i]["rows"] if x["rank"] < r["rank"]]
        used.add(r["id"]); left.remove(i)
    return out
if __name__ == "__main__":
    import json; P = json.load(open("inputs.json"))["providers"]
    for a in compute(P):
        print(a["resolved_step"], a["title"], "->", a["winner"], round(a["winner_row"]["total"], 2), "skipped", a["skipped"])
