from __future__ import annotations

import json
import re
import sqlite3
import importlib.util
import sys
from datetime import date
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]

sys.path.insert(0, str(ROOT / "scripts" / "health"))

from dashboard_v5.nutrition_mapping_review import build_review as build_mapping_review  # noqa: E402
from dashboard_v5 import read_api  # noqa: E402
from dashboard_v5.read_api import dispatch_api  # noqa: E402
from dashboard_v5.data_provider import build_bundle, connect  # noqa: E402
from dashboard_v5.render import render  # noqa: E402
fixture_spec = importlib.util.spec_from_file_location(
    "dashboard_v5_fixture", ROOT / "tests" / "fixtures" / "dashboard_v5_fixture.py"
)
assert fixture_spec and fixture_spec.loader
fixture_module = importlib.util.module_from_spec(fixture_spec)
fixture_spec.loader.exec_module(fixture_module)
build_dashboard_v5_fixture = fixture_module.build_dashboard_v5_fixture


TODAY = date(2026, 6, 15)
MAPPING_ALIAS = "SYNTHETIC_INCOMPLETE_FOOD_MAPPING_WITH_A_VERY_LONG_NAME_THAT_MUST_WRAP"


@pytest.fixture()
def fixture_db(tmp_path: Path) -> Path:
    db = tmp_path / "fixture.db"
    build_dashboard_v5_fixture(db, anchor_date=TODAY)
    return db


def mapping_preview(db: Path, alias: str = MAPPING_ALIAS) -> dict[str, object]:
    connection = sqlite3.connect(db)
    connection.row_factory = sqlite3.Row
    try:
        return next(
            group for group in build_mapping_review(connection)["groups"]
            if group["example_name"] == alias
        )
    finally:
        connection.close()


def mapping_payload(db: Path, **overrides: object) -> dict[str, object]:
    preview = mapping_preview(db)
    payload: dict[str, object] = {
        "version": 2, "action": "nutrition_mapping", "decision": "assign",
        "queue_key": preview["queue_key"], "alias": MAPPING_ALIAS,
        "canonical_food": "synthetic reviewed food", "sighi_score": 1,
        "confidence": "medium", "mapping_method": "manual_review",
        "source_label": "SIGHi", "source_version": "2026-test",
        "note": "synthetic reviewed mapping", "ingredient_review_required": False,
        "personal_tolerance_status": "unknown", "personal_tolerance_note": "",
        "target_revision": preview["target_revision"],
        "target_count": preview["target_count"],
        "target_day_count": preview["target_day_count"],
    }
    payload.update(overrides)
    return payload


def test_health_record_v5_uses_one_local_echarts_and_no_chartjs_or_svg_sparklines(fixture_db: Path) -> None:
    connection = connect(fixture_db)
    try:
        bundle = build_bundle(connection, today=TODAY.isoformat())
    finally:
        connection.close()
    html = render(bundle, health_record_6e=True)

    assert html.count("/health-assets/echarts-6.1.0.min.js") == 1
    assert "/health-assets/dashboard-v5-echarts.js" in html
    assert "chart.umd.min.js" not in html
    assert "<canvas id='cockpit-chart'" not in html
    assert "<canvas id='nutrition-chart'" not in html
    assert "<svg" not in html
    assert "cockpit-chart-table" in html
    assert "data-nutrition-trend-table" in html


def test_nutrition_read_apis_are_bounded_allowlisted_and_incomplete_not_zero(
    fixture_db: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    monkeypatch.setattr(read_api, "local_today", lambda: TODAY)

    days = dispatch_api(
        fixture_db,
        "/api/v1/nutrition/days",
        "from=2026-06-01&to=2026-06-15",
    )
    assert days["definition"]["insufficient_inputs"] == "no_index"
    assert "traffic_light" not in json.dumps(days)
    complete_days = [row for row in days["days"] if row["histamine"]["status"] == "complete"]
    assert [row["date"] for row in complete_days] == ["2026-06-05", "2026-06-06", "2026-06-07"]
    assert all(row["histamine"]["score"] == 6 for row in complete_days)
    assert all(row["histamine"]["mapping_coverage"] == 1.0 for row in complete_days)
    assert days["days"][-1]["histamine"]["score"] is None
    assert days["days"][-1]["histamine"]["status"] == "incomplete"

    detail = dispatch_api(fixture_db, "/api/v1/nutrition/day/2026-06-14", "")
    assert detail["coverage"]["status"] == "incomplete"
    assert detail["coverage"]["insufficient_inputs"] == "no_index"
    assert detail["macros"]["kcal"] is None
    assert set(detail["nutrients"]) <= set(read_api.NUTRIENT_ALLOWLIST)
    assert "nutrient.vitamin_c" not in detail["nutrients"]
    assert {meal["meal"] for meal in detail["meals"]} >= {"breakfast", "lunch", "dinner", "snack", "unassigned"}
    assert all("id" not in item and "raw" not in json.dumps(item).casefold() for item in detail["items"])

    complete_detail = dispatch_api(fixture_db, "/api/v1/nutrition/day/2026-06-05", "")
    assert {key: complete_detail["coverage"][key] for key in (
        "status", "item_count", "unknown_items", "mapping_coverage", "insufficient_inputs"
    )} == {
        "status": "complete",
        "item_count": 4,
        "unknown_items": 0,
        "mapping_coverage": 1.0,
        "insufficient_inputs": None,
    }
    assert {meal["meal"] for meal in complete_detail["meals"] if meal["item_count"]} == {
        "breakfast", "lunch", "dinner", "snack"
    }
    assert "nutrient.saturated" in complete_detail["nutrients"]
    assert len(complete_detail["items"]) == 4
    assert all(
        {"kcal", "protein_g", "carb_g", "fat_g"} <= set(item["macros"])
        and all(value is not None for value in item["macros"].values())
        and item["amount"] is not None
        and item["amount_unit"] == "g"
        and item["histamine"]["status"] == "mapped"
        and item["histamine"]["confidence"] == "high"
        for item in complete_detail["items"]
    )
    connection = sqlite3.connect(fixture_db)
    try:
        fixture_products = connection.execute(
            """SELECT COUNT(*),MIN(nutrient_count),MIN(provenance_count) FROM (
                   SELECT i.id,
                          (SELECT COUNT(*) FROM nutrition_item_nutrients n WHERE n.item_id=i.id) AS nutrient_count,
                          (SELECT COUNT(*) FROM nutrition_mapping_provenance p
                            WHERE p.alias=i.name AND p.affected_day=i.datum
                              AND p.confidence='high' AND p.source_version='fixture-v1') AS provenance_count
                     FROM nutrition_items i WHERE i.datum='2026-06-05'
               )"""
        ).fetchone()
        assert fixture_products == (4, 4, 1)
    finally:
        connection.close()
    serialized = json.dumps(detail, ensure_ascii=False).casefold()
    assert not any(forbidden in serialized for forbidden in ("raw_json", "source_product_id", "http://", "https://", "/home/", "/tmp/", ".hermes"))

    queue = dispatch_api(fixture_db, "/api/v1/nutrition/mapping-queue", "status=open")
    assert queue["items"][0]["queue_key"] == mapping_preview(fixture_db)["queue_key"]
    assert "source_product_id" not in json.dumps(queue)


def test_mapping_worker_is_transactional_idempotent_and_recomputes_only_affected_days(
    fixture_db: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    import health_dashboard_action_worker as worker

    monkeypatch.setattr(worker, "DASHBOARD_DB", fixture_db)
    monkeypatch.setattr(worker, "DASHBOARD_V5_FILE", None)
    connection = sqlite3.connect(fixture_db)
    try:
        connection.execute(
            """INSERT INTO nutrition_daily_summary_v2
               (datum,kcal,protein_g,carb_g,fat_g,item_count,histamine_score,histamine_max,histamine_unknown_count,histamine_label,nutrient_json)
               VALUES('2026-06-13',123,4,5,6,1,2,2,0,'classified','{}')"""
        )
        connection.commit()
    finally:
        connection.close()
    payload = mapping_payload(fixture_db, personal_tolerance_status="documented_tolerated", personal_tolerance_note="documented local note")

    worker.apply_mapping_action(payload, "a" * 32)
    worker.apply_mapping_action(payload, "a" * 32)

    connection = sqlite3.connect(fixture_db)
    connection.row_factory = sqlite3.Row
    try:
        day = connection.execute("SELECT kcal,protein_g,histamine_unknown_count,histamine_score,histamine_label,nutrient_json FROM nutrition_daily_summary_v2 WHERE datum='2026-06-14'").fetchone()
        assert day["kcal"] is None and day["protein_g"] is None
        assert day["histamine_unknown_count"] == 0
        assert day["histamine_score"] == 1.0
        assert day["histamine_label"] == "classified"
        nutrients = json.loads(day["nutrient_json"])
        assert nutrients["nutrient.fiber"] == 4.2
        assert "nutrient.vitamin_c" not in nutrients
        assert connection.execute("SELECT COUNT(*) FROM nutrition_mapping_action_log").fetchone()[0] == 1
        assert connection.execute("SELECT COUNT(*) FROM nutrition_mapping_provenance WHERE affected_day='2026-06-14'").fetchone()[0] == 1
        unaffected = connection.execute("SELECT kcal,histamine_score FROM nutrition_daily_summary_v2 WHERE datum='2026-06-13'").fetchone()
        assert dict(unaffected) == {"kcal": 123.0, "histamine_score": 2.0}
        source_row = connection.execute(
            """SELECT canonical_food,sighi_score,confidence,reason
               FROM nutrition_histamine_scores WHERE item_id=2"""
        ).fetchone()
        assert dict(source_row) == {
            "canonical_food": "synthetic reviewed food",
            "sighi_score": 1,
            "confidence": "high",
            "reason": "synthetic reviewed mapping",
        }
        provenance = connection.execute(
            """SELECT decision,alias,confidence,mapping_method,source_label,source_version,note,
                      ingredient_review_required,personal_tolerance_status,personal_tolerance_note,
                      definition_version
               FROM nutrition_mapping_provenance WHERE action_id=?""",
            ("a" * 32,),
        ).fetchone()
        provenance_values = dict(provenance)
        definition_version = provenance_values.pop("definition_version")
        assert re.fullmatch(
            r"sighi_mapping_load_v2\|mapping_assignment_v1\|1\|[a-f0-9]{64}",
            definition_version,
        )
        assert provenance_values == {
            "decision": "assign",
            "alias": "SYNTHETIC_INCOMPLETE_FOOD_MAPPING_WITH_A_VERY_LONG_NAME_THAT_MUST_WRAP",
            "confidence": "high",
            "mapping_method": "sighi_reference",
            "source_label": "synthetic_sighi_fixture",
            "source_version": "fixture-v1",
            "note": "synthetic reviewed mapping",
            "ingredient_review_required": 0,
            "personal_tolerance_status": "documented_tolerated",
            "personal_tolerance_note": "documented local note",
        }
        personal = connection.execute(
            """SELECT canonical_food,personal_status,evidence_level,notes
               FROM personal_food_tolerance"""
        ).fetchone()
        assert dict(personal) == {
            "canonical_food": "synthetic reviewed food",
            "personal_status": "documented_tolerated",
            "evidence_level": "user_documented",
            "notes": "documented local note",
        }
    finally:
        connection.close()


def test_worker_rejects_manipulated_queue_filename(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    import health_dashboard_action_worker as worker

    inbox = tmp_path / "actions"
    inbox.mkdir(mode=0o700)
    manipulated = inbox / "manipulated.json"
    manipulated.write_text("{}", encoding="utf-8")
    manipulated.chmod(0o600)
    monkeypatch.setattr(worker, "ACTION_INBOX", inbox)
    with pytest.raises(ValueError, match="invalid filename"):
        worker.load_action(manipulated)


def test_composite_and_ignore_do_not_alter_mappings(fixture_db: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    import health_dashboard_action_worker as worker

    monkeypatch.setattr(worker, "DASHBOARD_DB", fixture_db)
    monkeypatch.setattr(worker, "DASHBOARD_V5_FILE", None)
    base = mapping_payload(fixture_db, canonical_food="", sighi_score="unknown", confidence="low", mapping_method="manual_review", note="synthetic decision", ingredient_review_required=True, personal_tolerance_status="unknown", personal_tolerance_note="")
    with pytest.raises(ValueError):
        worker.validate_mapping_payload({**base, "decision": "composite", "sighi_score": 1})
    worker.apply_mapping_action({**base, "decision": "composite"}, "b" * 32)
    refreshed = mapping_payload(
        fixture_db, canonical_food="", sighi_score="unknown", confidence="low",
        mapping_method="manual_review", note="synthetic decision",
        ingredient_review_required=False, personal_tolerance_status="unknown",
        personal_tolerance_note="",
    )
    worker.apply_mapping_action({**refreshed, "decision": "ignore"}, "c" * 32)

    connection = sqlite3.connect(fixture_db)
    try:
        assert connection.execute("SELECT sighi_score FROM nutrition_histamine_scores WHERE item_id=2").fetchone()[0] is None
        assert connection.execute("SELECT status FROM nutrition_composite_product_review").fetchone()[0] == "needs_ingredient_review"
        assert connection.execute("SELECT COUNT(*) FROM nutrition_mapping_action_log").fetchone()[0] == 2
        assert connection.execute("SELECT COUNT(*) FROM personal_food_tolerance").fetchone()[0] == 0
    finally:
        connection.close()


def test_mapping_submission_requires_one_time_csrf_and_exact_shape(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    monkeypatch.setenv("HEALTH_DASHBOARD_FILE", "/tmp/synthetic-v4.html")
    monkeypatch.setenv("HEALTH_DASHBOARD_ACTION_INBOX", str(tmp_path / "actions"))
    import health_dashboard_server as server

    monkeypatch.setattr(server, "local_today", lambda: TODAY)
    token = server.issue_csrf_token(now=100.0)
    form = {
        "csrf_token": [token],
        "return_to": ["v5"],
        "decision": ["assign"],
        "queue_key": ["a" * 32],
        "alias": ["SYNTHETIC_INCOMPLETE_FOOD_MAPPING_WITH_A_VERY_LONG_NAME_THAT_MUST_WRAP"],
        "canonical_food": ["synthetic reviewed food"],
        "sighi_score": ["1"],
        "confidence": ["medium"],
        "mapping_method": ["manual_review"],
        "source_label": ["SIGHi"],
        "source_version": ["2026-test"],
        "note": ["bounded note"],
        "ingredient_review_required": ["false"],
        "personal_tolerance_status": ["unclear"],
        "personal_tolerance_note": ["neutral note"],
        "target_revision": ["b" * 64],
        "target_count": ["1"],
        "target_day_count": ["1"],
    }
    payload, return_to = server.validate_mapping_submission(form)
    assert return_to == "v5"
    assert payload["decision"] == "assign"
    assert payload["personal_tolerance_status"] == "unclear"
    assert payload["personal_tolerance_note"] == "neutral note"
    with pytest.raises(ValueError):
        server.validate_mapping_submission({**form, "canonical_food": ["x", "y"]})
    with pytest.raises(ValueError):
        server.validate_mapping_submission({**form, "sighi_score": ["9"]})
    with pytest.raises(ValueError):
        server.validate_mapping_submission({**form, "queue_key": ["../manipulated"]})
    assert server.consume_csrf_token(token, now=101.0)
    assert not server.consume_csrf_token(token, now=102.0)


def test_unknown_personal_tolerance_does_not_create_personal_row(
    fixture_db: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    import health_dashboard_action_worker as worker

    monkeypatch.setattr(worker, "DASHBOARD_DB", fixture_db)
    monkeypatch.setattr(worker, "DASHBOARD_V5_FILE", None)
    payload = mapping_payload(fixture_db)

    worker.apply_mapping_action(payload, "d" * 32)

    connection = sqlite3.connect(fixture_db)
    try:
        assert connection.execute("SELECT COUNT(*) FROM personal_food_tolerance").fetchone()[0] == 0
        assert connection.execute("SELECT sighi_score FROM nutrition_histamine_scores WHERE item_id=2").fetchone()[0] == 1
    finally:
        connection.close()


def test_recompute_rejects_untrusted_histamine_classifications(
    fixture_db: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    import health_dashboard_action_worker as worker

    monkeypatch.setattr(worker, "DASHBOARD_DB", fixture_db)
    connection = sqlite3.connect(fixture_db)
    connection.row_factory = sqlite3.Row
    try:
        connection.execute(
            """UPDATE nutrition_histamine_scores
                  SET canonical_food=NULL,sighi_score=3,traffic_light='unknown',confidence='high'
                WHERE item_id=2"""
        )
        worker.recompute_nutrition_day(connection, "2026-06-14")
        connection.commit()
        summary = connection.execute(
            """SELECT histamine_score,histamine_unknown_count,histamine_label
                 FROM nutrition_daily_summary_v2 WHERE datum='2026-06-14'"""
        ).fetchone()
        assert tuple(summary) == (None, 1, "unknown")
    finally:
        connection.close()


def test_mapping_exact_groups_keep_similar_legacy_collision_separate(
    fixture_db: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    import health_dashboard_action_worker as worker

    monkeypatch.setattr(worker, "DASHBOARD_DB", fixture_db)
    monkeypatch.setattr(worker, "DASHBOARD_V5_FILE", None)
    collision_name = "SYNTHETIC-INCOMPLETE-FOOD-MAPPING-WITH-A-VERY-LONG-NAME-THAT-MUST-WRAP"
    connection = sqlite3.connect(fixture_db)
    try:
        cursor = connection.execute(
            """INSERT INTO nutrition_items
               (source,source_item_id,datum,meal,name,kcal,protein_g,carb_g,fat_g,item_hash)
               VALUES('fixture','collision','2026-06-13','dinner',?,100,5,10,2,'fixture-collision')""",
            (collision_name,),
        )
        collision_id = int(cursor.lastrowid)
        connection.execute(
            """INSERT INTO nutrition_histamine_scores
               (item_id,canonical_food,sighi_score,traffic_light,confidence,reason)
               VALUES(?, 'synthetic prior food', 2, 'classified', 'high', 'synthetic prior mapping')""",
            (collision_id,),
        )
        connection.commit()
    finally:
        connection.close()
    payload = mapping_payload(fixture_db, source_label="synthetic manual source", source_version="fixture-v1", note="synthetic collision probe")
    worker.apply_mapping_action(payload, "e" * 32)
    connection = sqlite3.connect(fixture_db)
    try:
        assert connection.execute(
            "SELECT canonical_food,sighi_score FROM nutrition_histamine_scores WHERE item_id=?",
            (collision_id,),
        ).fetchone() == ("synthetic prior food", 2)
        assert connection.execute(
            "SELECT canonical_food,sighi_score FROM nutrition_histamine_scores WHERE item_id=2"
        ).fetchone() == ("synthetic reviewed food", 1)
    finally:
        connection.close()


def test_sighi_reference_uses_trusted_local_rule_provenance(
    fixture_db: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    import health_dashboard_action_worker as worker

    monkeypatch.setattr(worker, "DASHBOARD_DB", fixture_db)
    monkeypatch.setattr(worker, "DASHBOARD_V5_FILE", None)
    payload = mapping_payload(fixture_db, confidence="high", mapping_method="sighi_reference", source_label="untrusted client label", source_version="untrusted-client-version", note="synthetic trusted provenance probe")
    with pytest.raises(RuntimeError, match="local reference"):
        worker.apply_mapping_action({**payload, "sighi_score": 2}, "0" * 32)
    worker.apply_mapping_action(payload, "f" * 32)
    connection = sqlite3.connect(fixture_db)
    try:
        assert connection.execute(
            """SELECT source_label,source_version FROM nutrition_mapping_action_log
                 WHERE action_id=?""",
            ("f" * 32,),
        ).fetchone() == ("synthetic_sighi_fixture", "fixture-v1")
    finally:
        connection.close()
