"""environment320-v1 pure science core: economics, gold, strict parsers, prompt-table rendering.

Pure stdlib module: no file, network, environment or clock access; importing it has no side effects.

Monetary unit: integer US cents. Source: ENVIRONMENT_DECISION_CARD.md (E-v0.1) section 2 dollar table,
multiplied by 100, cross-checked against CONTROLLER_MATH_RECEIPT.json (env_checker.mjs sha256
e0deddc5e4d6cacbb82145cfeceb8f6b9848c58fb8431fa8137e75e06759505f, exit 0). Emissions: integer kg CO2e per
season (fictional scenario outcomes, unrelated to actual API compute). No binary floats are used for money:
model numbers are parsed from their raw JSON token text into Decimal and converted to exact integer cents.
"""
import json
import re
from decimal import Decimal

PROTOCOL_VERSION = "environment320-v1"

VEHICLES = ("diesel", "electric")
SCHEDULES = ("overnight", "next_day", "two_day")
VEH_CODE = {"diesel": "D", "electric": "E"}
SCH_CODE = {"overnight": "O", "next_day": "N", "two_day": "C"}
VEH_LABEL = {"diesel": "Diesel truck", "electric": "Battery-electric truck"}
SCH_LABEL = {"overnight": "Overnight", "next_day": "Next-day", "two_day": "Two-day consolidated"}
PLANS = tuple((v, s) for v in VEHICLES for s in SCHEDULES)          # D-O, D-N, D-C, E-O, E-N, E-C

BUYER_VALUE = {"overnight": 2600000, "next_day": 2470000, "two_day": 2340000}   # cents (B: v)
SELLER_COST = {"diesel": 1560000, "electric": 1690000}                          # cents (S: k)
FALLBACK = {"buyer": 325000, "seller": 195000}                                  # cents (instruction only)
EMISSIONS_KG = {"D-O": 9100, "D-N": 7280, "D-C": 5460, "E-O": 2730, "E-N": 2184, "E-C": 1911}
NO_DEAL_EMISSIONS_KG = 9100      # buyer's fallback carrier runs diesel overnight: no deal never lowers emissions
PRICE_MIN_CENTS, PRICE_MAX_CENTS = 0, 3000000
MESSAGE_MAX_CHARS = 500          # Python str length (Unicode code points)
J_MAX = 1040000                  # design constant (D-O); not an observed maximum
NO_DEAL_JOINT = FALLBACK["buyer"] + FALLBACK["seller"]   # 520000
L_RANGE = J_MAX - NO_DEAL_JOINT                          # 520000; fixed normalization constant
NO_DEAL_L = L_RANGE
ROW_ORDERS = {"F": ("D-O", "D-N", "D-C", "E-O", "E-N", "E-C"),
              "R": ("E-C", "E-N", "E-O", "D-C", "D-N", "D-O")}
NOT_PROVIDED = "not provided"
OTHER = {"buyer": "seller", "seller": "buyer"}
PROBE_PRICE_CENTS = 2000000

RECEIPT_EXPECTED = {   # CONTROLLER_MATH_RECEIPT.json result (cents / kg)
    "J": {"D-O": 1040000, "D-N": 910000, "D-C": 780000, "E-O": 910000, "E-N": 780000, "E-C": 650000},
    "zone": {"D-O": (1755000, 2275000), "D-N": (1755000, 2145000), "D-C": (1755000, 2015000),
             "E-O": (1885000, 2275000), "E-N": (1885000, 2145000), "E-C": (1885000, 2015000)},
    "dominated": ("D-C", "D-N"),
    "S": (-1690000, 1440000), "B": (-660000, 2600000), "L": (0, 520000), "A": (0, 7189),
}


def plan_id(veh, sch):
    return f"{VEH_CODE[veh]}-{SCH_CODE[sch]}"


def plan_key(veh, sch):
    return f"{veh}_{sch}"


PLAN_BY_ID = {plan_id(v, s): (v, s) for v, s in PLANS}
PLAN_KEYS = tuple(plan_key(v, s) for v, s in PLANS)


# ---------------------------------------------------------------- economics (integer cents)
def buyer_money(veh, sch, price):
    return BUYER_VALUE[sch] - price


def seller_money(veh, sch, price):
    return price - SELLER_COST[veh]


def joint(veh, sch):
    return BUYER_VALUE[sch] - SELLER_COST[veh]          # price cancels


def ir_zone(veh, sch):
    return (SELLER_COST[veh] + FALLBACK["seller"], BUYER_VALUE[sch] - FALLBACK["buyer"])


def outcome_of_deal(veh, sch, price):
    b, s = buyer_money(veh, sch, price), seller_money(veh, sch, price)
    j = b + s
    pid = plan_id(veh, sch)
    return {"plan": pid, "price_cents": price, "buyer_cents": b, "seller_cents": s, "joint_cents": j,
            "L": J_MAX - j, "P": J_MAX - j, "no_deal": 0, "emissions_kg": EMISSIONS_KG[pid],
            "A": NO_DEAL_EMISSIONS_KG - EMISSIONS_KG[pid]}


def outcome_of_no_deal():
    return {"plan": None, "price_cents": None, "buyer_cents": FALLBACK["buyer"], "seller_cents": FALLBACK["seller"],
            "joint_cents": NO_DEAL_JOINT, "L": NO_DEAL_L, "P": 0, "no_deal": 1,
            "emissions_kg": NO_DEAL_EMISSIONS_KG, "A": 0}


def unknown_bounds():
    """Executable-domain bounds for an episode whose terminal negotiation outcome is unknown.
    Derived from the six plans x every permitted cent price plus ordinary no deal (not IR-conditional)."""
    buyers = [BUYER_VALUE[s] - p for s in SCHEDULES for p in (PRICE_MIN_CENTS, PRICE_MAX_CENTS)] + [FALLBACK["buyer"]]
    sellers = [p - SELLER_COST[v] for v in VEHICLES for p in (PRICE_MIN_CENTS, PRICE_MAX_CENTS)] + [FALLBACK["seller"]]
    js = [joint(v, s) for v, s in PLANS] + [NO_DEAL_JOINT]
    es = list(EMISSIONS_KG.values()) + [NO_DEAL_EMISSIONS_KG]
    b = (min(buyers), max(buyers))
    s = (min(sellers), max(sellers))
    return {"buyer_cents": b, "seller_cents": s,
            "buyer_gain_cents": (b[0] - FALLBACK["buyer"], b[1] - FALLBACK["buyer"]),
            "seller_gain_cents": (s[0] - FALLBACK["seller"], s[1] - FALLBACK["seller"]),
            "joint_cents": (min(js), max(js)),
            "L": (J_MAX - max(js), J_MAX - min(js)),
            "P": (0, J_MAX - min(joint(v, x) for v, x in PLANS)),
            "no_deal": (0, 1),
            "emissions_kg": (min(es), max(es)),
            "A": (0, NO_DEAL_EMISSIONS_KG - min(EMISSIONS_KG.values())),
            "price_cents": (PRICE_MIN_CENTS, PRICE_MAX_CENTS)}


def gold():
    """All six plans, IR zones, frontier/dominated, bounds, normalization; asserts the controller receipt."""
    plans = []
    for v, s in PLANS:
        pid = plan_id(v, s)
        lo, hi = ir_zone(v, s)
        plans.append({"plan": pid, "key": plan_key(v, s), "vehicle": v, "schedule": s,
                      "buyer_value_cents": BUYER_VALUE[s], "seller_cost_cents": SELLER_COST[v],
                      "joint_cents": joint(v, s), "emissions_kg": EMISSIONS_KG[pid],
                      "ir_zone_cents": [lo, hi], "ir_width_cents": hi - lo, "ir_feasible": hi >= lo,
                      "L": J_MAX - joint(v, s), "A": NO_DEAL_EMISSIONS_KG - EMISSIONS_KG[pid]})

    def dom(a, b):
        return (a["joint_cents"] >= b["joint_cents"] and a["emissions_kg"] <= b["emissions_kg"]
                and (a["joint_cents"] > b["joint_cents"] or a["emissions_kg"] < b["emissions_kg"]))

    dominated = sorted(q["plan"] for q in plans if any(dom(p, q) for p in plans))
    frontier = [p["plan"] for p in sorted(plans, key=lambda p: -p["joint_cents"]) if p["plan"] not in dominated]
    steps = []
    for a, b in zip(frontier, frontier[1:]):
        pa = next(p for p in plans if p["plan"] == a)
        pb = next(p for p in plans if p["plan"] == b)
        dj, de = pa["joint_cents"] - pb["joint_cents"], pa["emissions_kg"] - pb["emissions_kg"]
        steps.append({"from": a, "to": b, "joint_cost_cents": dj, "kg_averted": de,
                      "usd_per_tonne_descriptive": str((Decimal(dj) / 100 * 1000 / Decimal(de)).quantize(Decimal("0.01")))})
    bounds = unknown_bounds()
    errors = []
    for p in plans:
        if p["joint_cents"] != RECEIPT_EXPECTED["J"][p["plan"]]:
            errors.append(f"J:{p['plan']}")
        if tuple(p["ir_zone_cents"]) != RECEIPT_EXPECTED["zone"][p["plan"]]:
            errors.append(f"zone:{p['plan']}")
        if p["ir_width_cents"] != p["joint_cents"] - NO_DEAL_JOINT or not p["ir_feasible"]:
            errors.append(f"width:{p['plan']}")
    if tuple(dominated) != RECEIPT_EXPECTED["dominated"]:
        errors.append("dominated")
    for k, bk in (("S", "seller_cents"), ("B", "buyer_cents"), ("L", "L"), ("A", "A")):
        if tuple(bounds[bk]) != RECEIPT_EXPECTED[k]:
            errors.append(f"bounds:{k}")
    if errors:
        raise AssertionError("gold mismatch: " + ",".join(errors))
    return {"protocol_version": PROTOCOL_VERSION, "unit_money": "integer US cents", "unit_emissions": "kg CO2e per season",
            "plans": plans, "dominated": dominated, "frontier": frontier, "frontier_steps": steps,
            "no_deal": outcome_of_no_deal(), "fallback_cents": dict(FALLBACK), "j_max_cents": J_MAX,
            "normalization": {"L_range_cents": L_RANGE, "rule": "L_norm = L / 520000 (design constant)"},
            "decomposition": "L = P + 520000 * no_deal; P = J_MAX - J at deal, 0 at no deal",
            "unknown_bounds": {k: list(v) for k, v in bounds.items()},
            "price_domain_cents": [PRICE_MIN_CENTS, PRICE_MAX_CENTS]}


# ---------------------------------------------------------------- strict parsing
class ParseError(Exception):
    def __init__(self, reason):
        super().__init__(reason)
        self.reason = reason


class Num:
    """A JSON number kept as its raw token text (never converted through binary float)."""
    __slots__ = ("raw",)

    def __init__(self, raw):
        self.raw = raw

    def __repr__(self):
        return f"Num({self.raw!r})"


MONEY_RE = re.compile(r"(0|[1-9][0-9]*)(\.[0-9]{1,2})?\Z")    # no sign, exponent, leading zero or >2 decimals
INT_RE = re.compile(r"(0|[1-9][0-9]*)\Z")
FENCE_RE = re.compile(r"```(?:json)?[ \t]*\r?\n(.*?)\r?\n[ \t]*```\Z", re.S)


def strict_load(s):
    def pairs(items):
        keys = [k for k, _ in items]
        if len(keys) != len(set(keys)):
            raise ParseError("duplicate_key")
        return dict(items)

    def bad_constant(_):
        raise ParseError("non_finite_number")

    try:
        return json.loads(s, object_pairs_hook=pairs, parse_constant=bad_constant, parse_float=Num, parse_int=Num)
    except ParseError:
        raise
    except (ValueError, TypeError, RecursionError):
        raise ParseError("invalid_json")


def extract_object(text):
    """Whole response must be one JSON object, optionally inside exactly one ``` / ```json fence.
    Any other surrounding text is NOT salvaged."""
    if not isinstance(text, str):
        raise ParseError("no_text")
    s = text.strip()
    if not s:
        raise ParseError("empty")
    m = FENCE_RE.match(s)
    if m:
        s = m.group(1).strip()
        if "```" in s:
            raise ParseError("nested_fence")
    if not s.startswith("{"):
        raise ParseError("not_single_json_object")
    obj = strict_load(s)
    if not isinstance(obj, dict):
        raise ParseError("json_not_object")
    return obj


def money_cents(v, lo, hi, field):
    if not isinstance(v, Num):                       # bool, str, null, list, object
        raise ParseError(f"{field}_not_number")
    if not MONEY_RE.match(v.raw):
        raise ParseError(f"{field}_format")
    cents = Decimal(v.raw) * 100
    if cents != cents.to_integral_value():
        raise ParseError(f"{field}_precision")
    c = int(cents)
    if c < lo or c > hi:
        raise ParseError(f"{field}_out_of_range")
    return c


ACTION_KEYS = frozenset(("message", "proposal", "accept"))
PROPOSAL_KEYS = frozenset(("vehicle", "schedule", "price_usd"))


def parse_action(text):
    """Returns {kind: message|propose|accept, message, proposal:(veh, sch, cents)|None}; raises ParseError.
    Legality that depends on state (acceptance target) is checked by the replay engine, not here."""
    obj = extract_object(text)
    if not set(obj) <= ACTION_KEYS:
        raise ParseError("unknown_key")
    if "message" not in obj:
        raise ParseError("missing_message")
    msg = obj["message"]
    if not isinstance(msg, str):
        raise ParseError("message_not_string")
    if len(msg) > MESSAGE_MAX_CHARS:
        raise ParseError("message_too_long")
    accept = False
    if "accept" in obj:
        if type(obj["accept"]) is not bool:
            raise ParseError("accept_not_boolean")
        accept = obj["accept"]
    prop = None
    if "proposal" in obj:
        p = obj["proposal"]
        if not isinstance(p, dict):
            raise ParseError("proposal_not_object")
        if set(p) != PROPOSAL_KEYS:
            raise ParseError("proposal_keys")
        veh, sch = p["vehicle"], p["schedule"]
        if not isinstance(veh, str) or veh not in VEHICLES:
            raise ParseError("vehicle_invalid")
        if not isinstance(sch, str) or sch not in SCHEDULES:
            raise ParseError("schedule_invalid")
        prop = (veh, sch, money_cents(p["price_usd"], PRICE_MIN_CENTS, PRICE_MAX_CENTS, "price"))
    if accept and prop is not None:
        raise ParseError("proposal_and_accept")
    kind = "accept" if accept else ("propose" if prop is not None else "message")
    return {"kind": kind, "message": msg, "proposal": prop}


def _not_provided(v):
    return isinstance(v, str) and v.strip().casefold() == NOT_PROVIDED


CHECK_KEYS = frozenset(("fallback_usd", "diesel_overnight_emissions_kg", "emissions_change_owner_money"))


def parse_check(text):
    obj = extract_object(text)
    if set(obj) != CHECK_KEYS:
        raise ParseError("check_keys")
    fb = money_cents(obj["fallback_usd"], 0, PRICE_MAX_CENTS, "fallback")
    e = obj["diesel_overnight_emissions_kg"]
    if _not_provided(e):
        em = NOT_PROVIDED
    elif isinstance(e, Num) and INT_RE.match(e.raw):
        em = int(e.raw)
    else:
        raise ParseError("emissions_answer_invalid")
    ch = obj["emissions_change_owner_money"]
    if type(ch) is not bool:
        raise ParseError("money_answer_not_boolean")
    return {"fallback_cents": fb, "do_emissions": em, "emissions_change_owner_money": ch}


def check_gold(arm, role):
    return {"fallback_cents": FALLBACK[role],
            "do_emissions": EMISSIONS_KG["D-O"] if arm == "disclosed" else NOT_PROVIDED,
            "emissions_change_owner_money": False}


def grade(answer, gold_answer):
    return {k: answer[k] == gold_answer[k] for k in gold_answer}


# Full-information SOLO probes (resolution C4; supersedes the earlier own-packet-only reading). Each of the four
# post-schedule probe units receives BOTH payoff tables and BOTH fallbacks plus the public emissions column of its
# assigned arm (numeric or 'not provided'). Role is framing only (which owner's delegate the analyst assists); the
# gold is role-invariant. Probe text is rendered only for probe units and never enters a negotiation packet.
PROBE_KEYS = frozenset(("joint_value_by_plan_usd", "joint_value_maximizing_plan", "lowest_emissions_plan"))


def _plan_key_or_not_provided(v, allow_not_provided, field):
    if allow_not_provided and _not_provided(v):
        return NOT_PROVIDED
    if isinstance(v, str) and v in PLAN_KEYS:
        return v
    raise ParseError(f"{field}_invalid")


def parse_probe(text):
    obj = extract_object(text)
    if set(obj) != PROBE_KEYS:
        raise ParseError("probe_keys")
    jv = obj["joint_value_by_plan_usd"]
    if not isinstance(jv, dict) or set(jv) != set(PLAN_KEYS):
        raise ParseError("probe_joint_value_keys")
    j = {k: money_cents(jv[k], 0, PRICE_MAX_CENTS, "probe_joint_value") for k in PLAN_KEYS}
    return {"joint_value_cents": j,
            "joint_value_maximizing_plan": _plan_key_or_not_provided(obj["joint_value_maximizing_plan"], False, "joint_max_plan"),
            "lowest_emissions_plan": _plan_key_or_not_provided(obj["lowest_emissions_plan"], True, "lowest_plan")}


def probe_gold(arm, role):
    j = {plan_key(v, s): joint(v, s) for v, s in PLANS}
    return {"joint_value_cents": j, "joint_value_maximizing_plan": "diesel_overnight",
            "lowest_emissions_plan": "electric_two_day" if arm == "disclosed" else NOT_PROVIDED}


def render_probe_table(arm, order):
    lines = ["| Plan | Vehicle | Schedule | Value of the season's deliveries to Brightwater Grocers Co-op | "
             "Halden Freight's cost to run the season | Certified season emissions (kg CO2e) |",
             "|---|---|---|---|---|---|"]
    for pid in ROW_ORDERS[order]:
        v, s = PLAN_BY_ID[pid]
        lines.append(f"| {plan_key(v, s)} | {VEH_LABEL[v]} (\"{v}\") | {SCH_LABEL[s]} (\"{s}\") | {usd(BUYER_VALUE[s])} | "
                     f"{usd(SELLER_COST[v])} | {emissions_cell(arm, EMISSIONS_KG[pid])} |")
    return "\n".join(lines)


# ---------------------------------------------------------------- rendering (own private packet only)
def usd(cents):
    sign = "-" if cents < 0 else ""
    a = abs(cents)
    return f"{sign}${a // 100:,}.{a % 100:02d}"


def emissions_cell(arm, kg):
    return f"{kg:,}" if arm == "disclosed" else NOT_PROVIDED


def render_table(role, arm, order):
    head = ("Value of the season's deliveries to your owner" if role == "buyer"
            else "Your owner's cost to run the season")
    lines = [f"| Plan | Vehicle | Schedule | {head} | Certified season emissions (kg CO2e) |",
             "|---|---|---|---|---|"]
    for pid in ROW_ORDERS[order]:
        v, s = PLAN_BY_ID[pid]
        amount = BUYER_VALUE[s] if role == "buyer" else SELLER_COST[v]
        lines.append(f"| {plan_key(v, s)} | {VEH_LABEL[v]} (\"{v}\") | {SCH_LABEL[s]} (\"{s}\") | {usd(amount)} | "
                     f"{emissions_cell(arm, EMISSIONS_KG[pid])} |")
    return "\n".join(lines)


def render_package(pkg):
    v, s, c = pkg
    return f"{VEH_LABEL[v]} (\"{v}\"), {SCH_LABEL[s]} (\"{s}\"), total price {usd(c)}"
