#!/usr/bin/env python3
"""Daily health sync watchdog.

- Registers local inbox files in the health DB.
- Imports daily health emails from Gmail into `tagebuch` (correct DB/schema).
- Fetches YAZIO diary data for yesterday and stores a summary in `tagebuch` and `ernaehrung`.
- Regenerates dashboard/daily report when something changed.
- Prints a Telegram-friendly summary only if new data was imported or open reviews exist.
"""
from __future__ import annotations

import json
import os
import re
import sqlite3
import subprocess
import sys
from datetime import date, timedelta
from pathlib import Path

import requests

BASE = Path("/home/agent/.hermes/assets/Gesundheit")
DB = BASE / "health_data.db"
PIPELINE = BASE / "scripts" / "health_pipeline.py"
ACCOUNT = "friday.uplink@gmail.com"
GOG_SECRET_ENV = Path.home() / ".hermes" / "secrets" / "gog_keyring.env"
YAZIO_BASE_URL = "https://yzapi.yazio.com/v15"
YAZIO_CLIENT_ID = os.getenv("YAZIO_CLIENT_ID", "")
YAZIO_CLIENT_SECRET = os.getenv("YAZIO_CLIENT_SECRET", "")
YAZIO_EMAIL = os.getenv("YAZIO_USERNAME", "")
YAZIO_PASSWORD = os.getenv("YAZIO_PASSWORD", "")
ENV = {**os.environ, "HOME": "/home/agent", "XDG_CONFIG_HOME": "/home/agent/.config"}


def db() -> sqlite3.Connection:
    c = sqlite3.connect(DB)
    c.row_factory = sqlite3.Row
    return c


def load_gog_keyring_password() -> str:
    value = os.environ.get("GOG_KEYRING_PASSWORD", "").strip()
    if value:
        return value
    if not GOG_SECRET_ENV.is_file():
        raise RuntimeError(f"Missing gog keyring secret environment file: {GOG_SECRET_ENV}")
    if GOG_SECRET_ENV.stat().st_mode & 0o077:
        raise RuntimeError(f"Unsafe permissions on gog keyring secret file: {GOG_SECRET_ENV}")
    for raw_line in GOG_SECRET_ENV.read_text(encoding="utf-8").splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#"):
            continue
        if line.startswith("export "):
            line = line[7:].strip()
        key, sep, raw_value = line.partition("=")
        if sep and key.strip() == "GOG_KEYRING_PASSWORD":
            secret = raw_value.strip().strip('"').strip("'")
            if secret:
                return secret
            raise RuntimeError(f"GOG_KEYRING_PASSWORD empty in {GOG_SECRET_ENV}")
    raise RuntimeError(f"GOG_KEYRING_PASSWORD missing in {GOG_SECRET_ENV}")


def run(cmd: list[str], timeout: int = 120) -> subprocess.CompletedProcess[str]:
    env = ENV.copy()
    if cmd and Path(cmd[0]).name == "gog":
        env["GOG_KEYRING_PASSWORD"] = load_gog_keyring_password()
    return subprocess.run(cmd, env=env, text=True, capture_output=True, timeout=timeout)


def register_inbox() -> int:
    r = run([sys.executable, str(PIPELINE), "register-inbox"])
    m = re.search(r"Registriert:\s*(\d+)", r.stdout)
    return int(m.group(1)) if m else 0


def import_health_emails() -> int:
    query = os.getenv("HEALTH_EMAIL_QUERY", "subject:Gesundheitsdaten newer_than:2d")
    r = run(["gog", "-a", ACCOUNT, "gmail", "messages", "search", query, "--max", "10", "--json", "--no-input"])
    if r.returncode != 0:
        return 0
    try:
        data = json.loads(r.stdout)
    except Exception:
        return 0
    messages = data.get("messages") or data.get("results") or data.get("threads") or []
    imported = 0
    con = db()
    try:
        for msg in messages:
            mid = msg.get("id") or msg.get("messageId")
            if not mid:
                continue
            g = run(["gog", "-a", ACCOUNT, "gmail", "get", mid, "--json", "--format", "full", "--no-input"])
            if g.returncode != 0:
                continue
            try:
                full = json.loads(g.stdout)
            except Exception:
                continue
            subject = full.get("subject") or full.get("Subject") or msg.get("subject") or "Gesundheitsdaten"
            body = full.get("body") or full.get("text") or full.get("snippet") or msg.get("snippet") or ""
            body = re.sub(r"<[^>]+>", " ", body)
            body = re.sub(r"\s+", " ", body).strip()
            today = date.today().isoformat()
            dupe = con.execute("SELECT id FROM tagebuch WHERE datum=? AND kategori='gesundheit' AND notizen LIKE ?", (today, f"%gmail:{mid}%")).fetchone()
            if dupe:
                continue
            con.execute("INSERT INTO tagebuch(datum,kategori,titel,inhalt,wirkung,verknuepft,notizen) VALUES(?,?,?,?,?,?,?)", (today, "gesundheit", subject[:120], body, "", "gmail", f"Quelle: gmail:{mid}"))
            imported += 1
        con.commit()
    finally:
        con.close()
    return imported


def import_yazio() -> int:
    day = (date.today() - timedelta(days=1)).isoformat()
    con = db()
    try:
        if con.execute("SELECT id FROM tagebuch WHERE datum=? AND kategori='ernaehrung' AND verknuepft='yazio'", (day,)).fetchone():
            return 0
    finally:
        con.close()
    try:
        auth = requests.post(f"{YAZIO_BASE_URL}/oauth/token", json={"client_id": YAZIO_CLIENT_ID, "client_secret": YAZIO_CLIENT_SECRET, "username": YAZIO_EMAIL, "password": YAZIO_PASSWORD, "grant_type": "password"}, timeout=30)
        if auth.status_code != 200:
            return 0
        token = auth.json()["access_token"]
        diary = requests.get(f"{YAZIO_BASE_URL}/user/consumed-items", params={"date": day}, headers={"Authorization": f"Bearer {token}"}, timeout=30)
        if diary.status_code != 200:
            return 0
        products = diary.json().get("products", []) + diary.json().get("simple_products", [])
        if not products:
            return 0
    except Exception:
        return 0
    lines = []
    by_meal = {}
    for item in products:
        meal = str(item.get("daytime") or "unknown").lower()
        amount = item.get("amount") or item.get("serving_quantity") or ""
        name = item.get("name") or item.get("product_name") or item.get("product_id") or "YAZIO item"
        text = f"{name} ({amount}g)" if amount else str(name)
        by_meal.setdefault(meal, []).append(text)
        lines.append(f"- {meal}: {text}")
    summary = "YAZIO-Ernährung " + day + "\n" + "\n".join(lines)
    con = db()
    try:
        con.execute("INSERT INTO tagebuch(datum,kategori,titel,inhalt,wirkung,verknuepft,notizen) VALUES(?,?,?,?,?,?,?)", (day, "ernaehrung", f"YAZIO-Ernährung: {day}", summary, "", "yazio", "Automatischer YAZIO Sync"))
        for meal, entries in by_meal.items():
            con.execute("INSERT INTO ernaehrung(datum,mahlzeit,beschreibung,wirkung,notizen) VALUES(?,?,?,?,?)", (day, meal, "; ".join(entries), "", "YAZIO"))
        con.commit()
    finally:
        con.close()
    return len(products)


def open_reviews() -> int:
    con = db()
    try:
        return con.execute("SELECT count(*) FROM laborwerte_staging WHERE status IN ('zur_pruefung','extrahiert')").fetchone()[0]
    finally:
        con.close()


def regenerate_reports() -> None:
    for args in [["dashboard"], ["daily-report"], ["generate-lab-report"]]:
        run([sys.executable, str(PIPELINE)] + args, timeout=180)


def main() -> None:
    inbox = register_inbox()
    emails = import_health_emails()
    yazio = import_yazio()
    reviews = open_reviews()
    if inbox or emails or yazio or reviews:
        regenerate_reports()
        print("## Gesundheitsdaten-Sync")
        print(f"- Neue Inbox-Dateien registriert: {inbox}")
        print(f"- Neue Gesundheitsdaten-E-Mails importiert: {emails}")
        print(f"- Neue YAZIO-Items importiert: {yazio}")
        print(f"- Offene Labor-Korrekturen: {reviews}")
        print("- Dashboard und Berichte wurden aktualisiert.")


if __name__ == "__main__":
    main()
