#!/usr/bin/env python3
"""Fast daily symptom score logger for HealthManager nutrition correlation.

Purpose: make nutrition↔symptom analysis less ridiculous by adding one small daily
structured row set. Scores are 0..3:
  0 none, 1 mild, 2 moderate, 3 severe

Example:
  health_symptom_quick_add.py --db /path/to/health.db --aphthen 1 --gi 0 --fatigue 2 --skin 0 --eyes 0 --joints 1 --notes "ok day" --dashboard
"""
from __future__ import annotations

import argparse
import sqlite3
import subprocess
import sys
from datetime import date, datetime
from pathlib import Path
from zoneinfo import ZoneInfo

BASE = Path('/home/agent/.hermes/assets/Gesundheit')
DB = BASE / 'health_data.db'
CORRELATIONS = BASE / 'scripts' / 'multimodal_correlations.py'
PIPELINE = BASE / 'scripts' / 'health_pipeline.py'

LABELS = {
    'aphthen': 'Aphthen/Mundulzera',
    'gi': 'GI/Darm',
    'fatigue': 'Müdigkeit/Fatigue',
    'skin': 'Haut',
    'eyes': 'Augen',
    'joints': 'Gelenke',
    'vascular': 'Vaskulär/Thrombose-Warnzeichen',
}
SEVERITY = {0: 'keine (0)', 1: 'leicht (1)', 2: 'mittel (2)', 3: 'schwer (3)'}
LOCAL_TIMEZONE = ZoneInfo('Europe/Zurich')


def conn(database: Path) -> sqlite3.Connection:
    c = sqlite3.connect(database)
    c.row_factory = sqlite3.Row
    return c


def ensure_schema(c: sqlite3.Connection) -> None:
    c.execute('''CREATE TABLE IF NOT EXISTS symptom_log (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        datum TEXT NOT NULL,
        symptom TEXT NOT NULL,
        schwergrad TEXT,
        kontext TEXT,
        notizen TEXT,
        created_at TEXT DEFAULT (datetime('now','localtime'))
    )''')
    duplicate_groups = c.execute(
        """SELECT COUNT(*) FROM (
               SELECT datum,symptom,kontext FROM symptom_log
               GROUP BY datum,symptom,kontext HAVING COUNT(*) > 1
           )"""
    ).fetchone()[0]
    if duplicate_groups == 0:
        c.execute('''CREATE UNIQUE INDEX IF NOT EXISTS idx_symptom_log_daily_dimension
                     ON symptom_log(datum, symptom, kontext)''')


def upsert_scores(
    day: str,
    scores: dict[str, int],
    notes: str,
    context: str,
    database: Path,
) -> None:
    validate_entry(day, scores, notes)
    c = conn(database)
    try:
        ensure_schema(c)
        for key, score in scores.items():
            label = LABELS[key]
            existing = list(c.execute(
                """SELECT id FROM symptom_log
                   WHERE datum=? AND symptom=? AND kontext=? ORDER BY id""",
                (day, label, context),
            ))
            if len(existing) > 1:
                raise RuntimeError(
                    "Mehrere Legacy-Zeilen für denselben Tag und dieselbe Dimension; "
                    "keine automatische Zusammenführung oder Überschreibung."
                )
            if existing:
                c.execute(
                    """UPDATE symptom_log SET schwergrad=?,notizen=?,created_at=datetime('now','localtime')
                       WHERE id=?""",
                    (SEVERITY[score], notes, existing[0][0]),
                )
            else:
                c.execute(
                    """INSERT INTO symptom_log(datum,symptom,schwergrad,kontext,notizen,created_at)
                       VALUES(?,?,?,?,?,datetime('now','localtime'))""",
                    (day, label, SEVERITY[score], context, notes),
                )
        c.commit()
    finally:
        c.close()


def local_today() -> date:
    return datetime.now(LOCAL_TIMEZONE).date()


def validate_entry(day: str, scores: dict[str, int], notes: str) -> None:
    if not isinstance(day, str):
        raise ValueError('Datum muss kanonischer ISO-Text sein.')
    try:
        parsed_day = date.fromisoformat(day)
    except ValueError as exc:
        raise ValueError('Ungültiges ISO-Datum.') from exc
    if parsed_day.isoformat() != day or parsed_day.year < 2000 or parsed_day > local_today():
        raise ValueError('Datum liegt ausserhalb des erlaubten Bereichs.')
    if not isinstance(scores, dict) or not scores or not set(scores).issubset(LABELS):
        raise ValueError('Unbekannte oder fehlende Symptomdimensionen.')
    if any(type(value) is not int or value not in range(4) for value in scores.values()):
        raise ValueError('Symptomwerte müssen ganze Zahlen von 0 bis 3 sein.')
    if not isinstance(notes, str) or len(notes) > 300:
        raise ValueError('Notiz darf höchstens 300 Zeichen enthalten.')


def refresh(dashboard: bool, database: Path) -> None:
    target = database.resolve()
    if dashboard and target != DB.resolve():
        raise ValueError('Dashboard-Pipeline für eine abweichende Datenbank ist nicht freigegeben.')
    subprocess.run(
        [sys.executable, str(CORRELATIONS), '--db', str(target)],
        check=True,
    )
    if dashboard:
        subprocess.run([sys.executable, str(PIPELINE), 'dashboard'], check=True)


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument('--db', type=Path, required=True)
    ap.add_argument('--date', default=local_today().isoformat(), help='YYYY-MM-DD, default today')
    for key in LABELS:
        ap.add_argument(f'--{key}', type=int, choices=[0,1,2,3])
    ap.add_argument('--notes', default='')
    ap.add_argument('--context', default='daily_quick_score')
    ap.add_argument('--dashboard', action='store_true')
    args = ap.parse_args()
    if args.dashboard and args.db.resolve() != DB.resolve():
        ap.error('--dashboard ist für eine abweichende --db nicht freigegeben.')
    scores = {key: getattr(args, key) for key in LABELS if getattr(args, key) is not None}
    if not scores:
        ap.error('Mindestens eine Symptomdimension muss explizit angegeben werden; fehlende Dimensionen bleiben unbekannt.')
    try:
        validate_entry(args.date, scores, args.notes)
    except ValueError as exc:
        ap.error(str(exc))
    upsert_scores(args.date, scores, args.notes, args.context, args.db)
    refresh(args.dashboard, args.db)
    partial_sum = sum(scores.values())
    parts = ', '.join(f'{LABELS[k]}={v}' for k, v in scores.items())
    if args.context == 'daily_quick_score' and len(scores) == len(LABELS):
        score_text = f'complete=true; total={partial_sum}'
    else:
        score_text = f'complete=false; partial_sum={partial_sum}; unspecified=unknown'
    print(f'Symptom-Quick-Log gespeichert: {args.date}; {score_text}; {parts}')
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
