"""
Agentul econom din Lumeni Current (M8), implementare de referință.

Ideea: agentul lasă mai mulți bani în comunitate decât consumă.
  1. Codul decide tot ce se poate decide din reguli și calcule (0 lei).
  2. Jev (TypeSafe System One) dă judecăți tipizate cu probabilități, pentru triaj.
  3. Claude intră numai când valoarea așteptată a pasului depășește de câteva ori costul lui:
     Haiku pentru răspunsuri și extrageri, Opus pentru analize cu propunere.
  4. Omul aprobă orice efect financiar sau asupra echipamentelor (M8.5).
  5. Fiecare apel intră în registru cu tokenii și costul lui, ca orice alt cost.

Rulare:  pip install anthropic httpx
         export TYPESAFE_API_KEY=...  ANTHROPIC_API_KEY=...
         python cost_aware_agent.py --demo      # ziua simulată, fără apeluri reale
         python cost_aware_agent.py             # apeluri reale pe cele trei exemple

Cheile stau pe server. Nimic din acest fișier nu rulează în aplicația membrului.
"""
from __future__ import annotations

import json
import os
import sys
from dataclasses import dataclass, field
from datetime import datetime

# ----------------------------------------------------------------------------- prețuri
# Prețuri de listă, octombrie 2026, în $ per milion de tokeni (intrare, ieșire).
PRICES = {
    "jev-latest": (0.042, 0.0),          # TypeSafe: ieșirea este gratuită
    "claude-haiku-5-5": (0.10, 0.50),
    "claude-opus-5-5": (4.00, 20.00),
}
RON_PER_USD = 4.5
OPERATOR_RON_PER_HOUR = 60.0
VALUE_MARGIN = 3.0                        # pasul scump rulează doar dacă valoarea ≥ 3 × cost


def usd_cost(model: str, tokens_in: int, tokens_out: int) -> float:
    pin, pout = PRICES[model]
    return (tokens_in * pin + tokens_out * pout) / 1e6


def ron(usd: float) -> float:
    return usd * RON_PER_USD


def operator_ron(minutes: float) -> float:
    return minutes / 60 * OPERATOR_RON_PER_HOUR


# ----------------------------------------------------------------------------- registru
@dataclass
class Entry:
    when: str
    event: str
    tier: str                       # cod | jev | haiku | opus | om
    tokens_in: int = 0
    tokens_out: int = 0
    cost_ron: float = 0.0
    operator_min: float = 0.0       # timp de om consumat
    saved_min: float = 0.0          # timp de om economisit (estimare)
    avoided_ron: float = 0.0        # erori de decont evitate (estimare)
    note: str = ""


@dataclass
class Ledger:
    entries: list[Entry] = field(default_factory=list)

    def add(self, **kw) -> Entry:
        e = Entry(when=datetime.now().strftime("%H:%M"), **kw)
        self.entries.append(e)
        return e

    def cost_ron(self) -> float:
        return sum(e.cost_ron for e in self.entries) + sum(operator_ron(e.operator_min) for e in self.entries)

    def value_ron(self) -> float:
        return sum(operator_ron(e.saved_min) + e.avoided_ron for e in self.entries)

    def report(self) -> str:
        lines = [f"{e.when} {e.event[:38]:<38} {e.tier:<6} {e.tokens_in:>5}/{e.tokens_out:<5} {e.cost_ron:8.4f} lei  {e.note}" for e in self.entries]
        lines.append(f"TOTAL: cost {self.cost_ron():.2f} lei · valoare {self.value_ron():.2f} lei · net {self.value_ron() - self.cost_ron():+.2f} lei")
        return "\n".join(lines)


LEDGER = Ledger()
DEMO = "--demo" in sys.argv


# ----------------------------------------------------------------------------- Jev
TYPESAFE_URL = "https://api.typesafe.ai/v1/systemone"

POLICY = "Agentul răspunde numai din datele membrului și din registru; orice corecție sau acțiune cu efect financiar se propune omului."

MESSAGE_QUESTIONS = {
    "intentie": {
        "type": "choice",
        "instructions": "Ce vrea membrul din `mesaj`?",
        "criteria": {
            "explicatie_extras": "să înțeleagă o cifră din extrasul lui",
            "corectie": "contestă o cifră și vrea modificare sau bani înapoi",
            "echipament": "problemă cu invertorul, bateria sau gateway-ul",
            "document": "cere un document sau un raport",
            "altceva": "niciuna dintre cele de mai sus",
        },
    },
    "din_registru": {"type": "noul", "instructions": "Se poate răspunde complet și corect din `membru.extras`?"},
    "escaladare": {"type": "noul", "instructions": "Cere mesajul o decizie umană: bani de returnat, reclamație, ton ostil, situație legală?"},
    "urgenta": {"type": "score", "instructions": "Cât de repede trebuie tratat mesajul?",
                "criteria": ["poate aștepta câteva zile", "în ziua curentă", "în ora curentă", "imediat: siguranță sau bani"]},
}

DISCREPANCY_QUESTIONS = {
    "cauza": {
        "type": "choice",
        "instructions": "Care este cea mai probabilă cauză a diferenței dintre `telemetrie_kwh` și `contor_kwh`, dată `cronologia`?",
        "criteria": {
            "gol_date": "telemetria are intervale lipsă sau marcate estimate",
            "ora_de_vara": "diferența apare în jurul schimbării orei",
            "contor_inlocuit": "operatorul de distribuție a schimbat contorul în perioadă",
            "necunoscut": "nicio explicație vizibilă în date",
        },
    },
    "corectie_necesara": {"type": "noul", "instructions": "Trebuie recalculat extrasul membrului pentru această perioadă?"},
}


def jev(state, questions, model="jev-latest", event="") -> tuple[dict, Entry]:
    """O cerere, mai multe întrebări în paralel. Returnează răspunsurile și înregistrarea din registru."""
    if DEMO:
        answers = demo_jev(questions)
        usage = {"input_tokens": 380 + 60 * len(questions), "output_tokens": 4 * len(questions)}
    else:
        import httpx  # pip install httpx
        r = httpx.post(
            TYPESAFE_URL,
            headers={"Authorization": f"Bearer {os.environ['TYPESAFE_API_KEY']}", "Content-Type": "application/json"},
            json={"model": model, "state": state, "questions": questions},
            timeout=30,
        )
        r.raise_for_status()
        data = r.json()
        answers, usage = data["answers"], data["usage"]
    entry = LEDGER.add(event=event, tier="jev", tokens_in=usage["input_tokens"], tokens_out=usage.get("output_tokens", 0),
                       cost_ron=ron(usd_cost(model, usage["input_tokens"], usage.get("output_tokens", 0))))
    return answers, entry


# ----------------------------------------------------------------------------- Claude
_client = None


def claude(model: str, system: str, user: str, effort: str, event: str = "", max_tokens: int = 2000) -> tuple[str | None, Entry]:
    """Haiku la efort mic pentru răspunsuri și extrageri; Opus pentru analize, cu model de rezervă la refuz."""
    global _client
    tier = "haiku" if "haiku" in model else "opus"
    if DEMO:
        text, tin, tout = f"[răspuns {model} simulat]", (2000 if tier == "haiku" else 4000), (400 if tier == "haiku" else 2500)
        entry = LEDGER.add(event=event, tier=tier, tokens_in=tin, tokens_out=tout, cost_ron=ron(usd_cost(model, tin, tout)))
        return text, entry
    if _client is None:
        from anthropic import Anthropic   # sau AnthropicFoundry(api_key=..., resource=...) pe Microsoft Foundry
        _client = Anthropic()
    kwargs = dict(model=model, max_tokens=max_tokens, output_config={"effort": effort}, system=system,
                  messages=[{"role": "user", "content": user}])
    if model == "claude-opus-5-5":
        # Răspuns de rezervă pe alt model dacă cererea este refuzată de clasificatoarele de siguranță.
        # Pe Microsoft Foundry parametrul nu există: se folosește BetaRefusalFallbackMiddleware din SDK.
        resp = _client.beta.messages.create(betas=["server-side-fallback-2026-07-01"], fallbacks="default", **kwargs)
    else:
        resp = _client.messages.create(**kwargs)
    tin, tout = resp.usage.input_tokens, resp.usage.output_tokens
    price_model = resp.model if resp.model in PRICES else model
    entry = LEDGER.add(event=event, tier=tier, tokens_in=tin, tokens_out=tout, cost_ron=ron(usd_cost(price_model, tin, tout)))
    if resp.stop_reason == "refusal":
        entry.note = "refuzat"
        return None, entry
    text = "".join(block.text for block in resp.content if block.type == "text")
    return text, entry


# ----------------------------------------------------------------------------- poarta economică
def est_cost_ron(model: str, tokens_in: int, tokens_out: int) -> float:
    return ron(usd_cost(model, tokens_in, tokens_out))


def worth_it(expected_value_ron: float, cost_ron: float) -> bool:
    """Pasul scump rulează doar dacă valoarea așteptată acoperă de VALUE_MARGIN ori costul."""
    return expected_value_ron >= VALUE_MARGIN * cost_ron


# ----------------------------------------------------------------------------- fluxuri
def handle_message(msg: str, member: dict) -> str:
    """Mesaj din aplicația membrului (M8.3)."""
    a, _ = jev({"mesaj": msg, "membru": member, "politica": POLICY}, MESSAGE_QUESTIONS, event=f"mesaj: {msg}")
    intent, conf = a["intentie"]["choice"], a["intentie"]["confidence"]
    from_ledger, needs_human = a["din_registru"]["noul"], a["escaladare"]["noul"]

    # 1. Bani sau reclamație: omul decide; Haiku pregătește rezumatul cazului.
    if needs_human >= 0.6 or intent == "corectie":
        brief, e = claude("claude-haiku-5-5", "Rezumă cazul pentru operator, în 5 rânduri, numai din date.",
                          json.dumps({"mesaj": msg, "membru": member}, ensure_ascii=False), effort="low", event="rezumat caz")
        e.saved_min = 4
        LEDGER.add(event="decizie operator", tier="om", operator_min=10, note="aprobare umană (M8.5)")
        return f"[către operator] {brief}"

    # 2. Șablon cu cifrele din registru: 0 lei.
    if intent in ("explicatie_extras", "document") and conf >= 0.85 and from_ledger >= 0.85:
        LEDGER.add(event="răspuns-șablon", tier="cod", saved_min=6)
        return template_answer(intent, member)

    # 3. Haiku explică din date, dacă merită cele 6 minute de operator pe care le înlocuiește.
    expected_value = operator_ron(6)
    if not worth_it(expected_value, est_cost_ron("claude-haiku-5-5", 2000, 400)):
        LEDGER.add(event="amânat pentru lotul de noapte", tier="cod")
        return "[amânat]"
    text, e = claude("claude-haiku-5-5", "Explică membrului, în română, numai din datele date. Fără promisiuni, fără cifre inventate.",
                     json.dumps({"mesaj": msg, "membru": member}, ensure_ascii=False), effort="low", event="răspuns Haiku")
    e.saved_min, e.note = 6, ("de verificat prin sondaj" if conf < 0.6 else "")
    return text or "[refuzat: către operator]"


def handle_discrepancy(site: dict) -> str:
    """Diferență telemetrie–contor peste prag (M2.4, M8.2)."""
    a, _ = jev(site, DISCREPANCY_QUESTIONS, event=f"reconciliere {site['site']}")
    cause, conf = a["cauza"]["choice"], a["cauza"]["confidence"]
    if cause == "ora_de_vara" and conf >= 0.85:
        LEDGER.add(event="corecție de ora de vară", tier="cod", saved_min=10)
        return "corectat automat (T-10)"
    # Miză mare: banii din extras. Opus pregătește propunerea; omul aprobă.
    expected_value = operator_ron(20) + abs(site["telemetrie_kwh"] - site["contor_kwh"]) * site.get("pret_lei_kwh", 1.05)
    if not worth_it(expected_value, est_cost_ron("claude-opus-5-5", 4000, 2500)):
        LEDGER.add(event="reconciliere amânată", tier="cod")
        return "[amânat]"
    proposal, e = claude("claude-opus-5-5", "Analizează intervalele și propune o corecție prin stornare, cu justificare pe date. Nu scrie în registru.",
                         json.dumps(site, ensure_ascii=False), effort="medium", event="propunere Opus")
    e.saved_min, e.avoided_ron = 20, abs(site["telemetrie_kwh"] - site["contor_kwh"]) * site.get("pret_lei_kwh", 1.05)
    LEDGER.add(event="aprobare corecție", tier="om", operator_min=3, note="omul aprobă stornarea (M4.4, M8.5)")
    return proposal or "[refuzat: către operator]"


def handle_invoice(document_text: str) -> dict:
    """Înrolare: Haiku extrage, Jev verifică fiecare câmp, Opus doar pe câmpurile semnalate (M8.1)."""
    raw, e = claude("claude-haiku-5-5", 'Extrage JSON cu cheile pod, putere_kw, furnizor din textul facturii. Doar JSON.',
                    document_text, effort="low", event="extragere factură", max_tokens=300)
    e.saved_min = 12
    fields = json.loads(raw) if raw and not DEMO else {"pod": "RO001E...7745", "putere_kw": 5.5, "furnizor": "Electrica"}
    checks, _ = jev({"factura": document_text, "extras": fields},
                    {k: {"type": "noul", "instructions": f"Este câmpul `extras.{k}` greșit față de `factura`?"} for k in fields},
                    event="verificare câmpuri")
    fired = {k: v["noul"] for k, v in checks.items() if v["noul"] > 0.7}
    if fired:
        fixed, e2 = claude("claude-opus-5-5", "Recitește factura și corectează doar câmpurile semnalate. Doar JSON.",
                           json.dumps({"factura": document_text, "extras": fields, "semnalate": fired}, ensure_ascii=False),
                           effort="medium", event="recitire Opus", max_tokens=300)
        e2.note = f"semnalate: {', '.join(fired)}"
        if fixed and not DEMO:
            fields = json.loads(fixed)
    LEDGER.add(event="confirmare câmpuri", tier="om", operator_min=1 if not fired else 2)
    return fields


def template_answer(intent: str, member: dict) -> str:
    x = member["extras"]
    if intent == "explicatie_extras":
        return (f"În {x['luna']} ai dat comunității {x['dat_kwh']} kWh și ai primit {x['primit_kwh']} kWh; din rețea ai luat "
                f"{x['retea_kwh']} kWh. De plată: {x['de_plata_lei']:.2f} lei. Liniile sunt în extras, rândurile 3–9.")
    return "Documentul cerut este generat din registru și atașat în aplicație."


# ----------------------------------------------------------------------------- demo
def demo_jev(questions: dict) -> dict:
    """Răspunsuri plauzibile pentru rularea fără chei; forma este cea a API-ului."""
    out = {}
    for k, q in questions.items():
        if q["type"] == "noul":
            out[k] = {"type": "noul", "noul": {"din_registru": 0.91, "escaladare": 0.06, "corectie_necesara": 0.8}.get(k, 0.1)}
        elif q["type"] == "choice":
            first = next(iter(q["criteria"]))
            out[k] = {"type": "choice", "choice": first, "confidence": 0.9, "probabilities": {c: (0.9 if c == first else 0.1 / (len(q["criteria"]) - 1)) for c in q["criteria"]}}
        else:
            out[k] = {"type": "score", "score": 1.2, "confidence": 0.7, "probabilities": {}}
    return out


if __name__ == "__main__":
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8")   # consola Windows
    member = {"rol": "prosumator", "extras": {"luna": "septembrie 2026", "de_plata_lei": 111.91, "dat_kwh": 188, "primit_kwh": 96, "retea_kwh": 143}}
    print(handle_message("De ce am plătit 112 lei în septembrie dacă am produs 410 kWh?", member))
    print(handle_discrepancy({"site": "GIR-009", "telemetrie_kwh": 377.2, "contor_kwh": 387.0, "pret_lei_kwh": 1.05,
                              "cronologia": ["13 oct: 4 intervale estimate", "contor citit de OD pe 20 oct"]}))
    print(handle_invoice("FACTURĂ ... POD RO001E...7745 ... putere 3,5 kW ... Electrica Furnizare ..."))
    print()
    print(LEDGER.report())
