from __future__ import annotations

import argparse
import asyncio
from pathlib import Path
import sys

PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

from app.models.google_workspace import GoogleWorkspacePreviewRequest
from app.services.operational_assistant_service import prepare_google_workspace_preview_with_trace
from app.services.operational_assistant_service import run_operational_assistant_with_trace
from app.services.tenant_store import SessionIdentity, get_tenant_store


def _build_session(tenant_slug: str) -> SessionIdentity:
    store = get_tenant_store()
    with store._connect_registry() as connection:
        row = connection.execute(
            """
            SELECT
                tenants.id AS tenant_id,
                tenants.slug AS tenant_slug,
                tenants.name AS tenant_name,
                tenants.database_path AS database_path,
                users.id AS user_id,
                users.email AS user_email,
                users.username AS username,
                users.name AS user_name,
                users.role AS role
            FROM tenants
            JOIN users ON users.tenant_id = tenants.id
            WHERE tenants.slug = ?
            ORDER BY users.created_at ASC
            LIMIT 1
            """,
            (tenant_slug,),
        ).fetchone()
        if row is None:
            raise SystemExit(f"Tenant non trovato: {tenant_slug}")
    session = store.build_service_session(row["tenant_id"])
    if session is None:
        raise SystemExit(f"Sessione di servizio non disponibile per tenant: {tenant_slug}")
    return session


async def _run_case(
    *,
    session: SessionIdentity,
    name: str,
    prompt: str,
    conversation: list[dict[str, str]],
    thread_state: dict[str, object] | None = None,
    must_contain: list[str],
    must_not_contain: list[str] | None = None,
) -> tuple[bool, list[dict[str, str]], dict[str, object] | None]:
    outcome = await run_operational_assistant_with_trace(
        session=session,
        message=prompt,
        conversation=conversation,
        thread_state=thread_state,
    )

    reply = outcome.reply
    errors: list[str] = []
    for token in must_contain:
        if token not in reply:
            errors.append(f"manca '{token}'")
    for token in must_not_contain or []:
        if token in reply:
            errors.append(f"non dovrebbe contenere '{token}'")

    status = "PASS" if not errors else "FAIL"
    print(f"[{status}] {name}")
    print(f"  route: {outcome.route}")
    print(f"  model: {outcome.model}")
    print(f"  prompt: {prompt}")
    print(f"  reply: {reply}")
    if errors:
        print(f"  errors: {', '.join(errors)}")
    print()

    next_conversation = conversation + [
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": reply},
    ]
    return (not errors), next_conversation, outcome.thread_state


async def main() -> int:
    parser = argparse.ArgumentParser(description="Smoke harness per l'assistente operativo del locale.")
    parser.add_argument("--tenant", default="ritual", help="Slug del tenant da usare, default ritual")
    args = parser.parse_args()

    session = _build_session(args.tenant)
    baco_session = _build_session("baco-lounge-bar")
    test_session = _build_session("test")
    passed = 0
    failed = 0

    ok, conversation, _ = await _run_case(
        session=session,
        name="compare-months",
        prompt="ho ordinato più prodotti in agosto 2025 o in settembre 2025?",
        conversation=[],
        must_contain=["agosto 2025", "settembre 2025"],
        must_not_contain=["catalogo prodotti del locale non trovo"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="orders-year-without-query",
        prompt="ho fatto degli ordini nel 2026?",
        conversation=[],
        must_contain=["2026"],
        must_not_contain=["Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="vodka-family",
        prompt="quante vodke abbiamo tra i prodotti?",
        conversation=[],
        must_contain=["vodka", "GREY GOOSE"],
        must_not_contain=["1 articoli"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="supplier-product-list",
        prompt="voglio una lista dei prodotti che acquistiamo da laconi",
        conversation=[],
        must_contain=["prodotti acquistati da LACONI", "REDBULL"],
        must_not_contain=["Fornitori piu presenti:", "Nel catalogo prodotti del locale risultano", "Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="supplier-product-list-followup-all",
        prompt="me li puoi mostrare tutti?",
        conversation=[
            {"role": "user", "content": "voglio una lista dei prodotti che acquistiamo da laconi"},
            {"role": "assistant", "content": "Questi sono i prodotti acquistati da LACONI: 59 prodotti distinti.\n- REDBULL: quantita totale 674, lotto ct\n- ... e altri 29 prodotti"},
        ],
        must_contain=["prodotti acquistati da LACONI", "59 prodotti distinti"],
        must_not_contain=["Non riesco ancora a leggere o aggiornare", "... e altri 29 prodotti"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, comparison_thread_state = await _run_case(
        session=session,
        name="purchase-comparison-percentage-quantity",
        prompt="quanto ho ordinato in più in percentuale parlando di pezzi di articoli acquistati in agosto 2025 rispetto a settembre 2025?",
        conversation=[],
        must_contain=["agosto 2025", "settembre 2025", "%"],
        must_not_contain=["0 prodotti distinti, 0 ordini distinti, quantita totale 0"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="purchase-comparison-followup-context",
        prompt="e tra luglio e agosto?",
        conversation=[
            {
                "role": "user",
                "content": "quanto ho ordinato in più in percentuale (parlando di pezzi di articoli acquistati) in agosto 2025 rispetto ad settembre 2025?",
            },
            {
                "role": "assistant",
                "content": (
                    "Confronto storico acquisti:\n"
                    "Hai ordinato una quantita totale maggiore in agosto 2025: 3570 contro 162.\n"
                    "In agosto 2025 hai ordinato il 2103,7% in piu rispetto a settembre 2025, parlando di pezzi totali acquistati."
                ),
            },
        ],
        thread_state=comparison_thread_state,
        must_contain=["luglio 2025", "agosto 2025", "%"],
        must_not_contain=["Nel catalogo prodotti del locale non trovo", "0 prodotti distinti, 0 ordini distinti, quantita totale 0"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="purchase-frequency-week-of-month",
        prompt="quanti ordini ho fatto nella prima settimana di agosto 2025?",
        conversation=[],
        must_contain=["01/08/2025", "07/08/2025", "ordini distinti"],
        must_not_contain=["362 righe trovate", "29/08/2025"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="purchase-batches-show-orders-totali",
        prompt="mostrami gli ordini di agosto 2025, a totali",
        conversation=[],
        must_contain=["Questi sono gli ordini rilevati per agosto 2025", "Ordine #"],
        must_not_contain=["Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="purchase-batches-followup-next-month",
        prompt="mostrami gli ordini di settembre 2025",
        conversation=[
            {"role": "user", "content": "mi mostri gli ordini che ho fatto in agosto 2025?"},
            {
                "role": "assistant",
                "content": (
                    "Questi sono gli ordini rilevati per agosto 2025:\n"
                    "- Ordine #106 del 2025-08-29 22:09:38: 5 righe, quantita totale 18\n"
                    "- Ordine #102 del 2025-08-24 06:08:59: 5 righe, quantita totale 20"
                ),
            },
        ],
        must_contain=["Questi sono gli ordini rilevati per settembre 2025", "Ordine #107"],
        must_not_contain=["Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="purchase-batch-detail-by-id",
        prompt="mostrami per esteso l' Ordine #96 del 2025-08-17 15:16:46: 15 righe, quantita totale 118",
        conversation=[],
        must_contain=["Ordine #96 del 2025-08-17 15:16:46", "Contenuto ordine:"],
        must_not_contain=["Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="purchase-batch-detail-by-date",
        prompt="mostrami l'ordine del 2025-08-17",
        conversation=[],
        must_contain=["Ordine #96", "Ordine #95"],
        must_not_contain=["Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="sales-goal-supplier-liters-followup",
        prompt="Moet & Chandon (come fornitore), obbiettivo 1000 litri, i prodotti sono tutti quelli del fornitore MOET, il target è solo a litri, l'anno è il 2026.",
        conversation=[
            {"role": "user", "content": "devo fissare un nuovo obbiettivo aziendale"},
            {
                "role": "assistant",
                "content": (
                    "Per impostare un nuovo obiettivo di vendita avrei bisogno di alcuni dettagli:\n"
                    "1. Nome dell’obiettivo.\n"
                    "2. Tipo di obiettivo.\n"
                    "3. Criterio di corrispondenza del prodotto o fornitore.\n"
                    "4. Target numerico.\n"
                    "5. Anno di riferimento."
                ),
            },
        ],
        must_contain=["MOET", "quante unita contiene", "cartone"],
        must_not_contain=["Nel catalogo prodotti del locale risultano", "corrispondono a moet chandon come obbiettivo"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="sales-goal-product-quantity-confirmation",
        prompt="Dom Perignon, 500 bottiglie da ordinare entro l'anno,",
        conversation=[
            {"role": "user", "content": "Che obbiettivi abbiamo?"},
            {"role": "assistant", "content": "Obiettivi 2026 configurati nel locale:\n- Target MOET 2026: 70.0/1000.0 L"},
            {"role": "user", "content": "Ne aggiungiamo uno?"},
            {
                "role": "assistant",
                "content": (
                    "Certo! Per aggiungere un nuovo obiettivo di vendita avrei bisogno di qualche dettaglio in più:\n"
                    "1. Nome dell’obiettivo (es. \"Bombay estate\")\n"
                    "2. Tipo di target: quantità, litri, litri_dual o nota\n"
                    "3. Cosa deve corrispondere il prodotto (query di ricerca)\n"
                    "4. Valore del target (numero)"
                ),
            },
        ],
        must_contain=["Per essere preciso", "Dom Perignon", "500 bottiglie", "CONFERMO"],
        must_not_contain=["Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, goal_conversation, goal_thread_state = await _run_case(
        session=test_session,
        name="sales-goal-confirm-seed",
        prompt="imposta obbiettivo 1000 litri dal fornitore ferro",
        conversation=[],
        must_contain=["Target ferro 2026", "1000 L", "CONFERMO"],
        must_not_contain=["Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, goal_conversation, goal_thread_state = await _run_case(
        session=test_session,
        name="sales-goal-confirm-apply",
        prompt="confermo",
        conversation=goal_conversation,
        thread_state=goal_thread_state,
        must_contain=["Target ferro 2026", "1000 L", "fornitore ferro"],
        must_not_contain=["Per essere preciso", "Se e corretto, scrivi CONFERMO"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, goal_conversation, goal_thread_state = await _run_case(
        session=test_session,
        name="sales-goal-read-after-confirm",
        prompt="mostrami gli obbiettivi",
        conversation=goal_conversation,
        thread_state=goal_thread_state,
        must_contain=["Obiettivi 2026 configurati nel locale", "Target ferro 2026"],
        must_not_contain=["Per essere preciso", "Se e corretto, scrivi CONFERMO"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=test_session,
        name="sales-goal-read-question-after-confirm",
        prompt="che obbiettivi abbiamo?",
        conversation=goal_conversation,
        thread_state=goal_thread_state,
        must_contain=["Obiettivi 2026 configurati nel locale", "Target ferro 2026"],
        must_not_contain=["Per essere preciso", "Se e corretto, scrivi CONFERMO"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, latest_orders_conversation, latest_orders_thread_state = await _run_case(
        session=test_session,
        name="latest-order-default",
        prompt="Ultimo ordine",
        conversation=[],
        must_contain=["Ordine #32", "2026-04-21 15:17:00"],
        must_not_contain=["Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, latest_orders_conversation, latest_orders_thread_state = await _run_case(
        session=test_session,
        name="latest-order-penultimate",
        prompt="Penultimo ordine",
        conversation=latest_orders_conversation,
        thread_state=latest_orders_thread_state,
        must_contain=["Ordine #31", "2026-04-06 10:16:00"],
        must_not_contain=["Ordine #32", "Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=test_session,
        name="latest-order-last-two",
        prompt="Ultimi 2 ordini fatti",
        conversation=[],
        must_contain=["Ordine #32", "Ordine #31"],
        must_not_contain=["Ordine #24", "Non trovo ordini che corrispondano alla richiesta."],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, vodka_conversation, vodka_thread_state = await _run_case(
        session=test_session,
        name="purchase-frequency-last-vodka-order",
        prompt="Da quanto non ordino vodka?",
        conversation=[],
        must_contain=["Non ordini vodka", "06/03/2026 10:15"],
        must_not_contain=["AMARO NONNINO", "Non riesco ancora a leggere o aggiornare"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=test_session,
        name="purchase-frequency-last-vodka-order-followup",
        prompt="Ma quando?",
        conversation=vodka_conversation,
        thread_state=vodka_thread_state,
        must_contain=["06/03/2026 10:15"],
        must_not_contain=["Non riesco ancora a leggere o aggiornare", "Prova a riformulare"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, nutella_conversation, nutella_thread_state = await _run_case(
        session=test_session,
        name="product-price-per-kg-seed",
        prompt="Quanto costa la Nutella al kg?",
        conversation=[],
        must_contain=["NUTELLA CARTUCCIA KG1X6PZ"],
        must_not_contain=["DA COMPLETARE"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, nutella_conversation, nutella_thread_state = await _run_case(
        session=test_session,
        name="product-price-per-kg-update",
        prompt="Aggiungi 11.31 come prezzo al kg per la nutella",
        conversation=nutella_conversation,
        thread_state=nutella_thread_state,
        must_contain=["Ho aggiornato il prodotto NUTELLA CARTUCCIA KG1X6PZ", "Prezzo al kg: € 11,31/kg"],
        must_not_contain=["Ho aggiunto il prodotto", "DA COMPLETARE"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=test_session,
        name="product-price-per-kg-delete-guardrail",
        prompt="No cancella questo nuovo prodotto",
        conversation=nutella_conversation,
        thread_state=nutella_thread_state,
        must_contain=["Non risulta nessun nuovo prodotto appena creato da eliminare"],
        must_not_contain=["Ho eliminato il prodotto"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, followup_conversation, followup_thread_state = await _run_case(
        session=session,
        name="history-month-context",
        prompt="che ordini ho fatto agosto scorso?",
        conversation=[],
        must_contain=["agosto 2025"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="history-follow-up",
        prompt="mi mostri i singoli ordini?",
        conversation=followup_conversation,
        thread_state=followup_thread_state,
        must_contain=["agosto 2025"],
        must_not_contain=["Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, catalog_conversation, catalog_thread_state = await _run_case(
        session=baco_session,
        name="catalog-detail-request",
        prompt="mostrami tutti i dati che abbiamo su giffard alla mora",
        conversation=[],
        must_contain=["Questi sono i dati che trovo su", "GIFFARD ALLA MORA CL100"],
        must_not_contain=["Non trovo acquisti che corrispondano alla richiesta", "Nel catalogo prodotti del locale risultano 19 articoli"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=baco_session,
        name="catalog-detail-followup-search",
        prompt="cerca nei prodotti",
        conversation=catalog_conversation,
        thread_state=catalog_thread_state,
        must_contain=["GIFFARD ALLA MORA CL100"],
        must_not_contain=["corrispondono a dati giffard mora cerca nei", "Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=baco_session,
        name="pack-size-exact-product",
        prompt="quante unit per pack ha giffard alla mora?",
        conversation=[],
        must_contain=["GIFFARD ALLA MORA CL100 (FERRO, bt): lotto bt", "GIFFARD ALLA MORA CL100 (FERRO, ct): 6 unita per pack"],
        must_not_contain=["GIFFARD POMPELMO", "SCIROPPO MANGO", "LIQUORE GIFFARD LIME CANE"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=baco_session,
        name="purchase-amount-overview-baco",
        prompt="quanto ho speso di ordini del 2026?",
        conversation=[],
        must_contain=["spesa totale", "€"],
        must_not_contain=["quantita totale 1", "Non trovo acquisti che corrispondano alla richiesta"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=session,
        name="tenant-sql-analytics-ranking",
        prompt="quali sono i primi 3 fornitori per numero di prodotti nel catalogo?",
        conversation=[],
        must_contain=["LACONI", "MOET", "supplier_name=", "total_products="],
        must_not_contain=["Questi sono i prodotti acquistati da", "Non trovo", "DOM PERIGNON"],
    )
    passed += int(ok)
    failed += int(not ok)

    salmone_conversation = [
        {"role": "user", "content": "che salmone compro?"},
        {
            "role": "assistant",
            "content": (
                "Nel catalogo prodotti del locale risultano 5 articoli che corrispondono a salmone:\n"
                "- SALMONE NORV. PREAF. KG.1 (CHEF, 1kg)\n"
                "- SALMONE AFF RITAGLI 500G S/VS (MARR, 500g)\n"
                "- FIL. SALMONE 1000/1600 G F (MARR, 1000/1600g)\n"
                "- FIL. SALMONE 1000/1600 g F (MARR, 1000/1600g)\n"
                "- FIL.SALMONE 1000/1400G G MOWI F (MARR, 1000/1400g)"
            ),
        },
    ]

    ok, _, _ = await _run_case(
        session=baco_session,
        name="catalog-lowest-price-followup",
        prompt="quale costa meno?",
        conversation=salmone_conversation,
        must_contain=["meno caro", "SALMONE AFF RITAGLI 500G S/VS", "€ 7,65"],
        must_not_contain=["Nel catalogo prodotti trovo questi prezzi ivati:"],
    )
    passed += int(ok)
    failed += int(not ok)

    ok, _, _ = await _run_case(
        session=baco_session,
        name="catalog-price-per-kg-followup",
        prompt="calcola il prezzo al chilo",
        conversation=salmone_conversation,
        must_contain=["Prezzo al chilo stimato", "/kg", "SALMONE"],
        must_not_contain=["Non trovo articoli che corrispondano a calcola chilo"],
    )
    passed += int(ok)
    failed += int(not ok)

    preview_outcome = await prepare_google_workspace_preview_with_trace(
        session,
        GoogleWorkspacePreviewRequest(
            kind="sheet",
            title="test",
            prompt="lista di tutti i prodotti food che compriamo, ordinata dal più economico al più costoso",
        ),
    )
    document_ok = preview_outcome.route == "documents-grounded-preview" and len(preview_outcome.preview.rows) > 0
    print(f"[{'PASS' if document_ok else 'FAIL'}] documents-grounded-preview")
    print(f"  route: {preview_outcome.route}")
    print(f"  model: {preview_outcome.model}")
    print(f"  rows: {len(preview_outcome.preview.rows)}")
    print(f"  summary: {preview_outcome.preview.summary}")
    print()
    passed += int(document_ok)
    failed += int(not document_ok)

    orders_preview_outcome = await prepare_google_workspace_preview_with_trace(
        session,
        GoogleWorkspacePreviewRequest(
            kind="sheet",
            title="ordini agosto",
            prompt="creami un file sheet con tutti gli ordini di agosto scorso",
        ),
    )
    orders_document_ok = (
        orders_preview_outcome.route == "documents-grounded-preview"
        and len(orders_preview_outcome.preview.rows) > 0
    )
    print(f"[{'PASS' if orders_document_ok else 'FAIL'}] documents-orders-grounded-preview")
    print(f"  route: {orders_preview_outcome.route}")
    print(f"  model: {orders_preview_outcome.model}")
    print(f"  rows: {len(orders_preview_outcome.preview.rows)}")
    print(f"  summary: {orders_preview_outcome.preview.summary}")
    print()
    passed += int(orders_document_ok)
    failed += int(not orders_document_ok)

    supplier_catalog_preview_outcome = await prepare_google_workspace_preview_with_trace(
        baco_session,
        GoogleWorkspacePreviewRequest(
            kind="sheet",
            title="chef",
            prompt="creami un file con la lista di tutti i prodotti che compro da Chef comprensiva di prezzi di acquisto",
        ),
    )
    supplier_document_ok = (
        supplier_catalog_preview_outcome.route == "documents-grounded-preview"
        and len(supplier_catalog_preview_outcome.preview.rows) >= 50
        and "catalogo locale" in str(supplier_catalog_preview_outcome.preview.summary or "")
    )
    print(f"[{'PASS' if supplier_document_ok else 'FAIL'}] documents-supplier-catalog-with-prices")
    print(f"  route: {supplier_catalog_preview_outcome.route}")
    print(f"  model: {supplier_catalog_preview_outcome.model}")
    print(f"  rows: {len(supplier_catalog_preview_outcome.preview.rows)}")
    print(f"  summary: {supplier_catalog_preview_outcome.preview.summary}")
    print()
    passed += int(supplier_document_ok)
    failed += int(not supplier_document_ok)

    print(f"Risultato: {passed} passati, {failed} falliti")
    return 0 if failed == 0 else 1


if __name__ == "__main__":
    raise SystemExit(asyncio.run(main()))
