"""Verktøylista og dispatchen som MCP-serveren deler mellom stdio (mcp_server.py) og HTTP (/mcp i app.py).
Alle verktøy kaller API-et under /v1 gjennom `kall(metode, sti, params, kropp)` som den som bruker modulen gir."""

FILTER = {"bransje": {"type": "string", "description": "Næringskode-prefiks (NACE), flere med komma, f.eks. 69.2,85.1"},
          "fylke": {"type": "string", "description": "Fylke(r), flere med komma"}, "kommune": {"type": "string"},
          "kundetype": {"type": "string", "enum": ["B2B", "B2C", "begge", "offentlig"]},
          "ansatte_min": {"type": "number"}, "ansatte_maks": {"type": "number"}, "omsetning_min": {"type": "number", "description": "Siste omsetning i kr"}, "omsetning_maks": {"type": "number"},
          "stiftet_min": {"type": "integer", "description": "Stiftelsesår fra"}, "stiftet_maks": {"type": "integer", "description": "Stiftelsesår til"},
          "utestaende_min": {"type": "number", "description": "Anslått utestående i kr"}, "kunder_min": {"type": "number", "description": "Anslått antall kunder"},
          "fakturaer_min": {"type": "number", "description": "Anslått fakturaer per år"},
          "kan_sms": {"type": "string", "enum": ["ja", "nei"]}, "har_dl_mobil": {"type": "string", "enum": ["ja", "nei"]}, "har_dl_epost": {"type": "string", "enum": ["ja", "nei"]},
          "har_dl_linkedin": {"type": "string", "enum": ["ja", "nei"]}, "har_nettside": {"type": "string", "enum": ["ja", "nei"]}, "har_regnskap": {"type": "string", "enum": ["ja", "nei"]},
          "fakturerer_mange": {"type": "string", "enum": ["ja", "nei"], "description": "Nettsida nevner purregebyr/inkasso/avtalegiro/efaktura"},
          "kjede": {"type": "string", "enum": ["ja", "nei"]}, "konsern": {"type": "string", "enum": ["ja", "nei"]},
          "signal": {"type": "string", "description": "Fakturasignal på nettsida: purregebyr, inkasso, avtalegiro, efaktura, abonnement, ehf …"},
          "regnskapsforer": {"type": "string", "description": "Navn på regnskapsfører (delstreng)"}, "revisor": {"type": "string", "description": "Navn på revisor (delstreng)"},
          "sorter": {"type": "string", "enum": ["omsetning", "ansatte", "utestaende", "kunder", "fakturaer", "nyeste", "navn"]},
          "antall": {"type": "integer", "description": "Treff per side, inntil 100"}, "side": {"type": "integer"},
          "antall_kun": {"type": "string", "enum": ["ja"], "description": "Bare tell treff. Gratis og teller ikke mot kvoten. Bruk den før et stort søk."},
          "status": {"type": "string", "enum": ["aktiv", "konkurs", "avvikling", "tvangsavvikling", "alle"], "description": "Standard aktiv. Konkurs og avvikling er med i datasettet som signal."},
          "register": {"type": "string", "enum": ["ja"], "description": "Søk i hele Enhetsregisteret (1,2 mill. enheter, bare navn/orgnr/sted/bransje/orgform) i stedet for hoveddatasettet — for å finne orgnr til små selskaper og ENK."}}

VERKTOY = [
    {"name": "sok_selskaper",
     "description": "Søk etter norske selskaper med ansatte. Fritekst (navn, orgnr, daglig leder) pluss filtre på bransje, sted, størrelse, stiftelsesår, kundetype, anslått utestående/kunder/fakturaer, fakturasignaler og om daglig leder har direktenummer/e-post/LinkedIn. Hvert treff teller som ett søketreff; antall_kun=ja er gratis.",
     "inputSchema": {"type": "object", "properties": {"q": {"type": "string", "description": "Fritekst eller orgnr"}, **FILTER}}},
    {"name": "berik_selskap",
     "description": "Alt om ett selskap (orgnr): register, siste regnskap, roller, konsern/kjede, anslag på utestående/kunder/fakturaer, fakturasignaler og daglig leders beste e-post og LinkedIn med kilde og sikkerhet. Mobilnummer vises som «tilgjengelig» til du kaller finn_mobil. 1 berikelse (dekning: full). Orgnr uten ansatte finnes i hele Enhetsregisteret og gir registerdata gratis (dekning: register).",
     "inputSchema": {"type": "object", "properties": {"orgnr": {"type": "string"}}, "required": ["orgnr"]}},
    {"name": "berik_selskaper",
     "description": "Samme som berik_selskap for inntil 100 orgnr i ett kall. Én berikelse per selskap som finnes; ukjente orgnr koster ingenting.",
     "inputSchema": {"type": "object", "properties": {"orgnrs": {"type": "array", "items": {"type": "string"}, "maxItems": 100}}, "required": ["orgnrs"]}},
    {"name": "sok_personer",
     "description": "Søk etter daglige ledere etter navn, med de samme filtrene som selskapssøket. Teller søketreff.",
     "inputSchema": {"type": "object", "properties": {"q": {"type": "string", "description": "Navn"}, **FILTER}}},
    {"name": "berik_person",
     "description": "Beste e-post, LinkedIn og andre selskaper til daglig leder. Gi orgnr, eller navn pluss firma eller domene. 1 berikelse når personen finnes.",
     "inputSchema": {"type": "object", "properties": {"orgnr": {"type": "string"}, "navn": {"type": "string"}, "firma": {"type": "string"}, "domene": {"type": "string"}}}},
    {"name": "finn_mobil",
     "description": "Direktenummer (mobil) til daglig leder med kilde og sikkerhetsgrad. Koster 1 telefonpoeng når nummer leveres, ingenting ellers. Si fra til brukeren før du henter mange.",
     "inputSchema": {"type": "object", "properties": {"orgnr": {"type": "string"}, "firma": {"type": "string"}, "domene": {"type": "string"}}}},
    {"name": "finn_epost",
     "description": "E-postadresse for et navn på et domene: kjent adresse hvis vi har den, ellers domenets bekreftede mønster, med sikkerhet og alternativer. 1 berikelse.",
     "inputSchema": {"type": "object", "properties": {"fornavn": {"type": "string"}, "etternavn": {"type": "string"}, "domene": {"type": "string"}, "orgnr": {"type": "string"}}, "required": ["fornavn", "etternavn"]}},
    {"name": "valider_epost",
     "description": "Valider inntil 100 e-postadresser: syntaks, domenet svarer (MX), rolleadresse, engangsdomene, kjent i datasettet. 1 berikelse per adresse.",
     "inputSchema": {"type": "object", "properties": {"eposter": {"type": "array", "items": {"type": "string"}, "maxItems": 100}}, "required": ["eposter"]}},
    {"name": "omvendt_epost",
     "description": "Hvilket selskap og hvilken leder hører en e-postadresse eller et domene til. 1 berikelse ved treff.",
     "inputSchema": {"type": "object", "properties": {"epost": {"type": "string"}, "domene": {"type": "string"}}}},
    {"name": "sjekk_liste",
     "description": "Sjekk brukerens egne kontakter eller kunder mot Firmasett: inntil 100 rader med orgnr/firma/domene, navn, e-post og telefon. Svarer per rad om selskapet og personen finnes, om e-post og telefon stemmer med det vi har (lik/avviker/vi_mangler/du_mangler) og gir beste e-post. 1 berikelse per rad med treff. Bruk den til å vaske et CRM eller fylle hull.",
     "inputSchema": {"type": "object", "properties": {"rader": {"type": "array", "maxItems": 100, "items": {"type": "object", "properties": {
         "orgnr": {"type": "string"}, "firma": {"type": "string"}, "domene": {"type": "string"}, "navn": {"type": "string"}, "epost": {"type": "string"}, "telefon": {"type": "string"},
         "linkedin": {"type": "string"}, "tittel": {"type": "string"}, "id": {"type": "string", "description": "Din egen id, sendes tilbake uendret"}}}}}, "required": ["rader"]}},
    {"name": "bransjer",
     "description": "Liste over næringskoder (2 siffer) med antall selskaper og dekning av daglig leders e-post og mobil, pluss fylkene. Gratis.",
     "inputSchema": {"type": "object", "properties": {}}},
    {"name": "konto",
     "description": "Plan, kvoter, forbruk i vinduet og telefonpoeng for nøkkelen. Gratis, teller ikke mot kvoten.",
     "inputSchema": {"type": "object", "properties": {}}},
    {"name": "bruk",
     "description": "Forbruk per dag siste 30 dager. Gratis.",
     "inputSchema": {"type": "object", "properties": {}}},
    {"name": "onboarding",
     "description": "Status for onboardingen av brukeren: hva som er lagret og hvilke spørsmål som gjenstår. Gratis. Kall denne først i en ny samtale hvis serverens instruksjoner sier at onboardingen ikke er fullført.",
     "inputSchema": {"type": "object", "properties": {}}},
    {"name": "lagre_profil",
     "description": "Lagre svarene fra onboardingen på kontoen: fullt navn, mobilnummer, jobb-e-post, firma, hva brukeren bestemmer over og hva brukeren påvirker beslutninger om. Kan kalles flere ganger med delvise svar. Gratis.",
     "inputSchema": {"type": "object", "properties": {"navn": {"type": "string", "description": "Fullt navn"}, "telefon": {"type": "string", "description": "Mobilnummer"},
                                                      "epost": {"type": "string", "description": "Jobb-e-post"}, "firma": {"type": "string", "description": "Firmanavn eller orgnr"},
                                                      "bestemmer": {"type": "string", "description": "Hva brukeren bestemmer over (ansvar, budsjett, innkjøp)"},
                                                      "paavirker": {"type": "string", "description": "Hva brukeren påvirker beslutninger om uten å bestemme selv"}}}},
]
ONBOARDING_SPORSMAL = [("navn", "Hva er fullt navn ditt?"), ("telefon", "Hvilket mobilnummer kan vi nå deg på?"), ("epost", "Hvilken jobb-e-post bruker du?"),
                       ("firma", "Hvilket firma jobber du i (navn eller orgnr)?"), ("bestemmer", "Hva bestemmer du over i jobben din (ansvar, budsjett, innkjøp)?"),
                       ("paavirker", "Hva påvirker du beslutninger om, uten å bestemme selv?")]
INSTRUKSJONER = ("Firmasett er norsk B2B-data, oppdatert hver natt: 66 000 selskaper med ansatte fullt beriket (siste regnskap, daglig leder med beste e-post, mobil og LinkedIn "
                 "med kilde og sikkerhetsgrad A–E), pluss hele Enhetsregisteret til oppslag på orgnr (bare registerdata). Alle svar har status ok|ikke_funnet|feil og meta med kvote igjen. ikke_funnet koster ingenting. "
                 "Mobilnumre koster telefonpoeng (1,50 kr): spør brukeren før du henter mange. Kjør sok_selskaper med antall_kun=ja før store søk. "
                 "SMS til mobil til en fysisk person krever samtykke (markedsføringsloven § 15); e-post til rolle i næringsvirksomhet er lov. Svar på norsk.")


def utfor(navn, a, kall):
    """Kjører ett verktøy mot API-et gjennom kall(metode, sti, params, kropp)."""
    a = a or {}
    if navn == "sok_selskaper":
        return kall("GET", "/v1/selskaper/sok", a, None)
    if navn == "berik_selskap":
        return kall("GET", f"/v1/selskaper/{a.get('orgnr', '')}", None, None)
    if navn == "berik_selskaper":
        return kall("POST", "/v1/selskaper/berik", None, a)
    if navn == "sok_personer":
        return kall("GET", "/v1/personer/sok", a, None)
    if navn == "berik_person":
        return kall("POST", "/v1/berik/person", None, a)
    if navn == "finn_mobil":
        return kall("POST", "/v1/telefon/finn", None, a)
    if navn == "finn_epost":
        return kall("POST", "/v1/epost/finn", None, a)
    if navn == "valider_epost":
        return kall("POST", "/v1/epost/valider", None, a)
    if navn == "omvendt_epost":
        return kall("GET", "/v1/epost/omvendt", a, None)
    if navn == "sjekk_liste":
        return kall("POST", "/v1/sjekk", None, a)
    if navn == "bransjer":
        return kall("GET", "/v1/bransjer", None, None)
    if navn == "konto":
        return kall("GET", "/v1/konto", None, None)
    if navn == "bruk":
        return kall("GET", "/v1/bruk", None, None)
    if navn == "onboarding":
        return kall("GET", "/v1/onboarding", None, None)
    if navn == "lagre_profil":
        return kall("POST", "/v1/onboarding", None, a)
    return {"status": "feil", "feil": {"kode": "ukjent_verktoy", "melding": f"Ukjent verktøy {navn}"}}


def onboarding_instruks(status):
    """Tillegg til instruksjonene når brukeren ikke har fullført onboardingen (fra GET /v1/onboarding)."""
    if not status or status.get("status") != "ok" or status["resultat"].get("fullfort"):
        return ""
    mangler = status["resultat"].get("mangler", [])
    sp = [q for f, q in ONBOARDING_SPORSMAL if f in mangler]
    return (" ONBOARDING IKKE FULLFØRT: Før du gjør oppslag for brukeren, still disse spørsmålene, gjerne ett eller to om gangen, og lagre svarene med lagre_profil: "
            + " ".join(f"({i + 1}) {q}" for i, q in enumerate(sp)) + " Forklar kort at svarene brukes til å tilpasse tjenesten og til å nå brukeren om kontoen. "
            "Brukeren kan hoppe over et spørsmål; lagre det du får. Når alt er lagret, fortsett med det brukeren egentlig spurte om.")


def handter(m, kall, instruksjoner=None):
    """Én JSON-RPC-melding inn, ett svar ut (None for varsler uten id)."""
    metode, id_, p = m.get("method"), m.get("id"), m.get("params") or {}
    if id_ is None:
        return None
    if metode == "initialize":
        instr = INSTRUKSJONER + (instruksjoner if instruksjoner is not None else onboarding_instruks(kall("GET", "/v1/onboarding", None, None)))
        return {"jsonrpc": "2.0", "id": id_, "result": {"protocolVersion": p.get("protocolVersion") or "2025-03-26", "capabilities": {"tools": {}},
                                                       "serverInfo": {"name": "firmasett", "version": "1.2"}, "instructions": instr}}
    if metode == "tools/list":
        return {"jsonrpc": "2.0", "id": id_, "result": {"tools": VERKTOY}}
    if metode == "tools/call":
        res = utfor(p.get("name"), p.get("arguments"), kall)
        import json
        return {"jsonrpc": "2.0", "id": id_, "result": {"content": [{"type": "text", "text": json.dumps(res, ensure_ascii=False, indent=1)}],
                                                       "isError": isinstance(res, dict) and res.get("status") == "feil"}}
    if metode == "ping":
        return {"jsonrpc": "2.0", "id": id_, "result": {}}
    return {"jsonrpc": "2.0", "id": id_, "error": {"code": -32601, "message": f"ukjent metode {metode}"}}
