"""
BAN YARO — Hintergrund-Scheduler
Täglich: Gesundheits-Erinnerungen per Push versenden.
"""

import logging
from datetime import date, datetime, timedelta
from zoneinfo import ZoneInfo
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger

_TZ = ZoneInfo("Europe/Berlin")

from database import db
from routes.push import send_push_to_user, send_push_to_all
import weather

logger = logging.getLogger(__name__)

_scheduler = AsyncIOScheduler(timezone="Europe/Berlin")

# In-Memory Job-Protokoll: {job_id: {"last_run": datetime, "result": str, "status": "ok"|"error"}}
_job_log: dict = {}


def start():
    _scheduler.add_job(
        _job_health_reminders,
        CronTrigger(hour=8, minute=0),   # täglich 08:00 Uhr
        id="health_reminders",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    _scheduler.add_job(
        _job_poison_archive,
        CronTrigger(hour=3, minute=0),   # täglich 03:00 Uhr (ruhige Zeit)
        id="poison_archive",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    _scheduler.add_job(
        _job_weather_alert,
        CronTrigger(hour=7, minute=30),  # täglich 07:30 Uhr
        id="weather_alert",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    _scheduler.add_job(
        _job_milestone_check,
        CronTrigger(hour=0, minute=5),   # täglich 00:05 Uhr
        id="milestone_check",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    _scheduler.add_job(
        _job_import_events,
        CronTrigger(day_of_week='sun', hour=2),  # jeden Sonntag 02:00 Uhr
        id="import_events",
        replace_existing=True,
        misfire_grace_time=7200,
    )

    # Einmalig beim Start (nach 10s Verzögerung) für sofortige Befüllung
    _scheduler.add_job(
        _job_import_events,
        'date',
        run_date=datetime.now(tz=_TZ) + timedelta(seconds=10),
        id="import_events_startup",
        replace_existing=True,
    )
    # Alle 4 Wochen Di 03:00 — Rassen aus TheDogAPI aktualisieren
    _scheduler.add_job(
        _job_seed_breeds,
        CronTrigger(day=1, hour=3, minute=0),  # 1. jedes Monats
        id="seed_breeds",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    # Alle 4 Wochen Di 04:00 — fehlende Rassen aus Wikidata ergänzen
    _scheduler.add_job(
        _job_seed_wikidata_breeds,
        CronTrigger(day=1, hour=4, minute=0),  # 1. jedes Monats
        id="seed_wikidata",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    # Jeden Montag 09:00 — Wöchentlicher Fortschritts-Lober
    _scheduler.add_job(
        _job_weekly_praise,
        CronTrigger(day_of_week='mon', hour=9, minute=0),
        id="weekly_praise",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    # Täglich 06:00 Uhr Status-Report per Mail
    _scheduler.add_job(
        _job_status_report,
        CronTrigger(hour=6, minute=0),
        id="status_report",
        replace_existing=True,
        misfire_grace_time=1800,
    )
    # Jeden Montag 07:00 — KI-Gesundheitsberichte (alle 2 Wochen)
    _scheduler.add_job(
        _job_ki_health_report,
        CronTrigger(day_of_week='mon', hour=7, minute=0),
        id="ki_health_report",
        replace_existing=True,
        misfire_grace_time=3600,
    )
    _scheduler.start()
    logger.info("Scheduler gestartet — Health-Reminder 08:00, Giftköder-Archiv 03:00, Wetter-Alert 07:30, Meilenstein-Check 00:05, Event-Import So 02:00, Rassen-Seed monatlich 1. des Monats, Status-Report täglich 06:00. OSM-Cache: on-demand (kein Prewarm).")


def stop():
    _scheduler.shutdown(wait=False)
    logger.info("Scheduler gestoppt.")


# ------------------------------------------------------------------
# JOB: Gesundheits-Erinnerungen
# ------------------------------------------------------------------
async def _job_health_reminders():
    """
    Findet alle Health-Einträge mit `naechstes`-Datum:
      - genau heute   → sofortige Erinnerung
      - in 7 Tagen    → Vorwarnung
      - gestern       → Überfällig-Erinnerung (nur einmal, 1 Tag nach Fälligkeit)
    Schickt jeweils eine Push-Notification an den Hundebesitzer.
    """
    today    = date.today()
    in7      = today + timedelta(days=7)
    yesterday = today - timedelta(days=1)

    logger.info(f"Health-Reminder Job läuft für {today}")

    with db() as conn:
        # Alle fälligen Einträge der nächsten 7 Tage + gestrige (überfällig)
        rows = conn.execute("""
            SELECT h.id, h.typ, h.bezeichnung, h.naechstes,
                   d.user_id, d.name AS hund_name
            FROM health h
            JOIN dogs d ON d.id = h.dog_id
            WHERE h.naechstes IN (?, ?, ?)
              AND h.typ IN ('impfung', 'entwurmung', 'medikament')
        """, (str(today), str(in7), str(yesterday))).fetchall()

    sent_total = 0
    for r in rows:
        naechstes = date.fromisoformat(r["naechstes"])
        delta     = (naechstes - today).days

        if delta == 7:
            title = f"⏰ Erinnerung: {r['bezeichnung']}"
            body  = f"In 7 Tagen fällig für {r['hund_name']}."
        elif delta == 0:
            title = f"📅 Heute fällig: {r['bezeichnung']}"
            body  = f"Bitte heute erledigen — {r['hund_name']} wartet."
        else:  # delta == -1 → gestern überfällig
            title = f"⚠️ Überfällig: {r['bezeichnung']}"
            body  = f"War gestern fällig für {r['hund_name']} — bitte bald erledigen."

        sent = send_push_to_user(r["user_id"], {
            "type":  "health_reminder",
            "title": title,
            "body":  body,
            "data":  {"page": "health"},
        })
        sent_total += sent
        if sent:
            logger.info(f"Reminder Push: user={r['user_id']} entry={r['id']} delta={delta}d")

    logger.info(f"Health-Reminder Job fertig — {len(rows)} Einträge, {sent_total} Push gesendet.")
    _log_job("health_reminders", "ok", f"{len(rows)} Einträge, {sent_total} Push")


# ------------------------------------------------------------------
# JOB: Abgelaufene Giftköder-Meldungen archivieren
# Abgelaufene, aber noch nicht manuell aufgelöste Einträge werden
# sauber als geloest=1 markiert — für spätere KI-Musteranalyse.
# Die Zeilen selbst werden NIE gelöscht.
# ------------------------------------------------------------------
async def _job_poison_archive():
    """
    Findet Giftköder-Meldungen deren expires_at verstrichen ist
    und die noch nicht als geloest markiert wurden.
    Setzt geloest=1, geloest_grund='automatisch_abgelaufen'.
    """
    from datetime import datetime
    now = datetime.utcnow().isoformat()
    with db() as conn:
        result = conn.execute("""
            UPDATE poison
            SET    geloest       = 1,
                   geloest_at    = datetime('now'),
                   geloest_grund = 'automatisch_abgelaufen'
            WHERE  geloest   = 0
              AND  expires_at < ?
        """, (now,))
        count = result.rowcount
    _log_job("poison_archive", "ok", f"{count} Meldungen archiviert")
    if count:
        logger.info(f"Giftköder-Archiv: {count} abgelaufene Meldungen archiviert.")


# ------------------------------------------------------------------
# JOB: Wetter-Alarm (Hitzepfoten / Gewitter)
# ------------------------------------------------------------------
async def _job_weather_alert():
    """
    Holt Tagesprognose für mehrere deutsche Städte.
    Sendet Push-Notification wenn:
      - Temperatur >= 28°C (Asphalt-Warnung für Pfoten)
      - Gewitter wahrscheinlich
    Hitze hat Vorrang: Bei Hitze wird kein Gewitter-Push mehr gesendet.
    """
    logger.info("Wetter-Alert Job läuft")
    try:
        summary = await weather.get_weather_summary()
    except Exception as e:
        logger.error(f"Wetter-Alert: Fehler beim Abruf: {e}")
        return

    max_temp = summary["max_temp_c"]
    thunderstorm = summary["thunderstorm"]

    if max_temp >= 28:
        _log_job("weather_alert", "ok", f"Hitze-Push: {max_temp:.0f}°C")
        sent = send_push_to_all({
            "type":  "weather_heat",
            "title": "☀️ Heißer Asphalt heute",
            "body":  f"Bis {max_temp:.0f}°C heute — Asphalt kann über 50°C heiß werden. Frühmorgens oder abends gassi gehen!",
            "data":  {"tag": "weather-heat"},
        })
        logger.info(f"Wetter-Alert Hitze: {max_temp:.1f}°C — {sent} Push gesendet.")
        return  # Kein Gewitter-Push mehr nötig wenn Hitze bereits gemeldet

    if thunderstorm:
        sent = send_push_to_all({
            "type":  "weather_thunder",
            "title": "⛈️ Gewitter möglich",
            "body":  "Heute Gewitter wahrscheinlich. Gassi-Tour früh einplanen und Hund beruhigen.",
            "data":  {"tag": "weather-thunder"},
        })
        logger.info(f"Wetter-Alert Gewitter — {sent} Push gesendet.")
        return

    logger.info("Wetter-Alert: Keine Warnung nötig heute.")
    _log_job("weather_alert", "ok", "Keine Warnung")


# ------------------------------------------------------------------
# JOB: Geburtstags- und Monats-Meilensteine
# Läuft täglich um 00:05 Uhr (Europe/Berlin).
# Prüft alle Hunde mit gesetztem Geburtstag und erstellt bei Treffern
# einen Tagebucheintrag (is_milestone=1) + Push-Notification.
# ------------------------------------------------------------------
async def _job_milestone_check():
    """
    Prüft für jeden Hund mit bekanntem Geburtstag ob heute ein
    Meilenstein-Tag ist:
      - Jahrestag (1. Geburtstag, 2. Geburtstag, …)
      - Monatsjubiläum in den ersten 12 Monaten (1 Monat, 2 Monate, …, 11 Monate)
    Doppelt-Schutz: Wenn bereits ein Meilenstein-Eintrag mit demselben
    Titel für heute existiert, wird kein zweiter erstellt.
    """
    today = date.today()
    logger.info(f"Meilenstein-Check läuft für {today}")

    with db() as conn:
        dogs = conn.execute("""
            SELECT d.id, d.name, d.user_id, d.geburtstag
            FROM dogs d
            WHERE d.geburtstag IS NOT NULL
              AND d.geburtstag != ''
        """).fetchall()

    created_total = 0

    for dog in dogs:
        try:
            bday = date.fromisoformat(dog["geburtstag"])
        except ValueError:
            logger.warning(f"Meilenstein: ungültiges Geburtstag für Hund {dog['id']}: {dog['geburtstag']!r}")
            continue

        milestone = _compute_milestone(today, bday, dog["name"])
        if milestone is None:
            continue

        titel, text = milestone

        with db() as conn:
            # Doppelt-Schutz: kein zweiter Eintrag am selben Tag mit gleichem Titel
            exists = conn.execute("""
                SELECT id FROM diary
                WHERE dog_id = ? AND datum = ? AND titel = ? AND is_milestone = 1
            """, (dog["id"], str(today), titel)).fetchone()

            if exists:
                logger.info(f"Meilenstein bereits vorhanden: Hund {dog['id']} '{titel}'")
                continue

            # Tagebucheintrag anlegen
            cur = conn.execute("""
                INSERT INTO diary (dog_id, datum, typ, titel, text, is_milestone)
                VALUES (?, ?, 'milestone', ?, ?, 1)
            """, (dog["id"], str(today), titel, text))
            entry_id = cur.lastrowid

            # Junction-Tabelle befüllen
            conn.execute("""
                INSERT OR IGNORE INTO diary_dogs (diary_id, dog_id) VALUES (?, ?)
            """, (entry_id, dog["id"]))

        # Push an Besitzer
        send_push_to_user(dog["user_id"], {
            "type":  "milestone",
            "title": titel,
            "body":  text,
            "data":  {"page": "diary"},
            "tag":   f"milestone-{dog['id']}-{today}",
        })

        logger.info(f"Meilenstein erstellt: Hund {dog['id']} '{titel}' → diary_id={entry_id}")
        created_total += 1

    logger.info(f"Meilenstein-Check fertig — {created_total} Einträge erstellt.")
    _log_job("milestone_check", "ok", f"{created_total} Meilensteine erstellt")


# ------------------------------------------------------------------
# JOB: VDH-Events importieren
# ------------------------------------------------------------------
async def _job_import_events():
    """
    Scrapt Veranstaltungen von vdh.de und importiert neue Events in die DB.
    Bereits vorhandene external_ids werden übersprungen (Upsert-Logik).
    """
    try:
        from scraper.events_vdh import fetch_vdh_events
    except ImportError as e:
        logger.error(f"Event-Import: Scraper konnte nicht geladen werden: {e}")
        return

    try:
        events = await fetch_vdh_events()
    except Exception as e:
        logger.error(f"Event-Import: Fehler beim Scrapen: {e}")
        return

    imported = 0
    with db() as conn:
        for ev in events:
            try:
                exists = conn.execute(
                    "SELECT id FROM events WHERE external_id = ?",
                    (ev['external_id'],)
                ).fetchone()
                if not exists:
                    conn.execute("""
                        INSERT INTO events (user_id, titel, datum, ort_name, typ, link, quelle, external_id, status)
                        VALUES (NULL, ?, ?, ?, ?, ?, 'vdh', ?, 'aktiv')
                    """, (
                        ev['titel'],
                        ev['datum'],
                        ev.get('ort_name'),
                        ev['typ'],
                        ev.get('link'),
                        ev['external_id'],
                    ))
                    imported += 1
            except Exception as e:
                logger.warning(f"Event-Import: Fehler beim Speichern von '{ev.get('titel')}': {e}")

    logger.info(f"Event-Import: {imported} neue Events importiert (von {len(events)} geparsten).")
    _log_job("import_events", "ok", f"{imported} neue von {len(events)} Events")


# ------------------------------------------------------------------
# JOB: Rassen aus TheDogAPI seeden
# ------------------------------------------------------------------
async def _job_seed_breeds():
    """Lädt alle Hunderassen von TheDogAPI und speichert sie in wiki_rassen."""
    try:
        from scraper.breeds import fetch_and_seed_breeds, mirror_breed_photos
    except ImportError as e:
        logger.error(f"Breed-Seed: Scraper konnte nicht geladen werden: {e}")
        return

    try:
        count = await fetch_and_seed_breeds()
        logger.info(f"Breed seed job done: {count} breeds")
        mirrored = await mirror_breed_photos()
        logger.info(f"Breed photo mirror done: {mirrored} photos")
    except Exception as e:
        logger.error(f"Breed-Seed: Fehler: {e}")


# ------------------------------------------------------------------
# JOB: Fehlende Rassen aus Wikidata ergänzen
# ------------------------------------------------------------------
async def _job_seed_wikidata_breeds():
    """Lädt fehlende Hunderassen von Wikidata und spiegelt Fotos lokal."""
    try:
        from scraper.wikidata_breeds import fetch_and_seed_wikidata_breeds, mirror_wikidata_photos
    except ImportError as e:
        logger.error(f"Wikidata-Seed: Scraper konnte nicht geladen werden: {e}")
        return

    try:
        count = await fetch_and_seed_wikidata_breeds()
        logger.info(f"Wikidata breed seed done: {count} neue Rassen")
        mirrored = await mirror_wikidata_photos()
        logger.info(f"Wikidata photo mirror done: {mirrored} Fotos")
        # Wikipedia-Fotos für Rassen die noch kein Bild haben
        from scraper.wikipedia_photos import fetch_wikipedia_photos
        wp_count = await fetch_wikipedia_photos()
        logger.info(f"Wikipedia photo fetch done: {wp_count} Fotos")
        _log_job("seed_wikidata", "ok", f"{count} Rassen, {mirrored}+{wp_count} Fotos")
    except Exception as e:
        logger.error(f"Wikidata-Seed: Fehler: {e}")
        _log_job("seed_wikidata", "error", str(e))

# ------------------------------------------------------------------
# Hilfsfunktion: Job-Protokoll aktualisieren
# ------------------------------------------------------------------
def _log_job(job_id: str, status: str, result: str):
    _job_log[job_id] = {
        "last_run": datetime.now(tz=_TZ),
        "status":   status,
        "result":   result,
    }


# ------------------------------------------------------------------
# Hilfsfunktion: Lob-Text für einen Hund generieren
# ------------------------------------------------------------------
async def _generate_praise_for_dog(dog: dict, user_id: int) -> str:
    """Generiert einen Lob-Text für einen Hund basierend auf der letzten Woche."""
    from ki import complete, KIUnavailableError
    import json as _json
    from datetime import date, timedelta

    since = (date.today() - timedelta(days=7)).isoformat()
    name  = dog["name"]
    rasse = dog.get("rasse") or "Hund"

    stats = {}
    try:
        with db() as conn:
            stats["diary"]    = conn.execute("SELECT COUNT(*) FROM diary WHERE dog_id=? AND datum>=?", (dog["id"], since)).fetchone()[0]
            stats["training"] = conn.execute("SELECT COUNT(*) FROM training_sessions WHERE dog_id=? AND datum>=?", (dog["id"], since)).fetchone()[0]
            stats["top_training"] = conn.execute("SELECT COUNT(*) FROM training_sessions WHERE dog_id=? AND datum>=? AND ist_top=1", (dog["id"], since)).fetchone()[0]
            stats["health"]   = conn.execute("SELECT COUNT(*) FROM health WHERE dog_id=? AND datum>=?", (dog["id"], since)).fetchone()[0]
            stats["days_active"] = conn.execute(
                "SELECT COUNT(DISTINCT datum) FROM diary WHERE dog_id=? AND datum>=?", (dog["id"], since)
            ).fetchone()[0]
            # Wie viele Wochen ist der User dabei?
            first = conn.execute("SELECT MIN(datum) FROM diary WHERE dog_id=?", (dog["id"],)).fetchone()[0]
            if first:
                weeks_total = max(1, (date.today() - date.fromisoformat(first)).days // 7)
            else:
                weeks_total = 1
            stats["weeks_total"] = weeks_total
    except Exception:
        pass

    # Prompt aufbauen
    aktivitaet_parts = []
    if stats.get("diary", 0):
        aktivitaet_parts.append(f"{stats['diary']} Tagebuch-Eintr\u00e4ge")
    if stats.get("training", 0):
        t = f"{stats['training']} Trainingseinheiten"
        if stats.get("top_training", 0):
            t += f" (davon {stats['top_training']} Top-Training)"
        aktivitaet_parts.append(t)
    if stats.get("health", 0):
        aktivitaet_parts.append(f"{stats['health']} Gesundheitseintr\u00e4ge")

    if not aktivitaet_parts:
        aktivitaet_text = "Diese Woche war ruhig \u2014 keine erfassten Aktivit\u00e4ten."
    else:
        aktivitaet_text = ", ".join(aktivitaet_parts)

    prompt = f"""Du bist ein warmer, wohlwollender Begleiter f\u00fcr Hundebesitzer. Schreibe eine kurze pers\u00f6nliche Lob-Nachricht (2-3 S\u00e4tze) f\u00fcr die vergangene Woche.

Hund: {name} ({rasse})
Letzte 7 Tage: {aktivitaet_text}
Dabei seit: {stats.get('weeks_total', 1)} Wochen

Regeln (unbedingt einhalten):
- Nur loben, NIEMALS Ratschl\u00e4ge geben oder auf Fehlendes hinweisen
- Sprich \u00fcber den Hund: "{name} hatte eine tolle Woche" \u2014 nicht \u00fcber den Besitzer
- Auch bei 0 Aktivit\u00e4ten: positive Formulierung (\u201eAuch ruhige Wochen geh\u00f6ren dazu\u201c)
- Maximal 3 kurze S\u00e4tze
- Warm, pers\u00f6nlich, keine Floskeln
- Kein "Du solltest...", kein "Vergiss nicht...", keine Empfehlungen"""

    try:
        text = await complete(
            prompt,
            system="Du schreibst kurze, warme Lob-Nachrichten f\u00fcr Hundebesitzer. Nur Lob, keine Ratschl\u00e4ge.",
            max_tokens=150,
        )
        return text.strip()
    except Exception:
        # Fallback wenn KI nicht verfügbar
        if aktivitaet_parts:
            return f"{name} hatte eine aktive Woche \u2014 {aktivitaet_text}. Das ist toll! \U0001f43e"
        else:
            return f"Auch ruhige Wochen geh\u00f6ren dazu. {name} wei\u00df, dass du f\u00fcr ihn da bist. \U0001f43e"


# ------------------------------------------------------------------
# JOB: Wöchentlicher Fortschritts-Lober
# ------------------------------------------------------------------
async def _job_weekly_praise():
    """Jeden Montag: Lob-Text f\u00fcr alle aktiven Hunde generieren + Push senden."""
    from datetime import date
    import json as _json

    today = date.today()
    d     = today.isocalendar()
    week_key = f"{d[0]}-W{d[1]:02d}"

    logger.info(f"Weekly Praise Job startet f\u00fcr Woche {week_key}")

    # Alle Hunde laden, für die noch kein Lob diese Woche existiert
    with db() as conn:
        dogs = conn.execute("""
            SELECT d.id, d.name, d.rasse, d.user_id, d.foto_url
            FROM dogs d
            WHERE NOT EXISTS (
                SELECT 1 FROM weekly_praise wp
                WHERE wp.dog_id=d.id AND wp.week_key=?
            )
            ORDER BY d.id
        """, (week_key,)).fetchall()

    dogs = [dict(d) for d in dogs]
    logger.info(f"Weekly Praise: {len(dogs)} Hunde ohne Lob diese Woche.")

    import asyncio
    generated = 0
    for dog in dogs:
        try:
            praise = await _generate_praise_for_dog(dog, dog["user_id"])
            with db() as conn:
                conn.execute("""
                    INSERT OR IGNORE INTO weekly_praise (user_id, dog_id, week_key, praise_text)
                    VALUES (?,?,?,?)
                """, (dog["user_id"], dog["id"], week_key, praise))

            # Push-Notification — erste 100 Zeichen als Preview
            preview = praise[:100] + "\u2026" if len(praise) > 100 else praise
            send_push_to_user(dog["user_id"], {
                "type":  "weekly_praise",
                "title": f"\U0001f43e R\u00fcckblick f\u00fcr {dog['name']}",
                "body":  preview,
                "data":  {"page": "diary"},
                "tag":   f"weekly-praise-{dog['id']}-{week_key}",
            })

            generated += 1
            await asyncio.sleep(2)  # Rate limiting für KI
        except Exception as e:
            logger.error(f"Weekly Praise: Fehler f\u00fcr Hund {dog['id']}: {e}")

    logger.info(f"Weekly Praise Job fertig \u2014 {generated}/{len(dogs)} Lob-Texte generiert.")
    _log_job("weekly_praise", "ok", f"{generated} Lob-Texte f\u00fcr KW {d[1]}")


# ------------------------------------------------------------------
# JOB: KI-Gesundheitsberichte (alle 2 Wochen, jeden Montag 07:00)
# ------------------------------------------------------------------
async def _job_ki_health_report():
    """
    Erstellt für jeden Hund, der seit mehr als 13 Tagen keinen KI-Gesundheitsbericht
    hat (oder noch keinen hatte), einen neuen Bericht via ki.health_summary() und
    schickt eine Push-Notification an den Besitzer. Maximal 20 Hunde pro Lauf.
    """
    import ki as KI

    with db() as conn:
        dogs = conn.execute("""
            SELECT d.id AS dog_id, d.name, d.rasse, d.geburtstag, d.gewicht_kg, d.user_id
            FROM dogs d
            WHERE d.id NOT IN (
                SELECT dog_id FROM ki_health_reports
                WHERE erstellt_at >= datetime('now', '-13 days')
            )
            ORDER BY d.id
            LIMIT 20
        """).fetchall()

    dogs = [dict(d) for d in dogs]
    if not dogs:
        logger.info("KI-Gesundheitsbericht: Keine fälligen Hunde.")
        _log_job("ki_health_report", "ok", "0 Berichte erstellt")
        return

    count = 0
    for dog in dogs:
        try:
            with db() as conn:
                health_rows = conn.execute(
                    "SELECT * FROM health WHERE dog_id=? ORDER BY datum DESC",
                    (dog["dog_id"],)
                ).fetchall()
            health_data = [dict(r) for r in health_rows]

            dog_info = {
                "name":        dog["name"],
                "rasse":       dog.get("rasse"),
                "geburtstag":  dog.get("geburtstag"),
                "gewicht_kg":  dog.get("gewicht_kg"),
            }

            bericht = await KI.health_summary(health_data=health_data, dog_info=dog_info)

            with db() as conn:
                conn.execute(
                    "INSERT INTO ki_health_reports (dog_id, user_id, bericht) VALUES (?, ?, ?)",
                    (dog["dog_id"], dog["user_id"], bericht)
                )

            send_push_to_user(dog["user_id"], {
                "type":  "ki_health_report",
                "title": f"Gesundheitsbericht für {dog['name']}",
                "body":  "Dein KI-Assistent hat einen neuen Bericht erstellt.",
                "data":  {"page": "health"},
            })

            count += 1
            logger.info(f"KI-Gesundheitsbericht: Bericht für Hund {dog['dog_id']} ({dog['name']}) erstellt.")
        except Exception as e:
            logger.error(f"KI-Gesundheitsbericht: Fehler für Hund {dog['dog_id']} ({dog['name']}): {e}")

    logger.info(f"KI-Gesundheitsbericht Job fertig — {count}/{len(dogs)} Berichte erstellt.")
    _log_job("ki_health_report", "ok", f"{count} Berichte erstellt")


# ------------------------------------------------------------------
# JOB: Status-Report per Mail (täglich 06:00 Uhr)
# ------------------------------------------------------------------
async def _job_status_report():
    """Sendet einen HTML-Status-Report an ADMIN_EMAIL."""
    import os
    from mailer import send_email

    admin = os.getenv("ADMIN_EMAIL", "")
    if not admin:
        logger.info("Status-Report: ADMIN_EMAIL nicht gesetzt, übersprungen.")
        return

    now_str = datetime.now(tz=_TZ).strftime("%d.%m.%Y %H:%M")

    # --- DB-Metriken abrufen ---
    metrics = {}
    try:
        with db() as conn:
            # Züchter
            try:
                metrics["zuchter_pending"]  = conn.execute("SELECT COUNT(*) FROM wiki_zuchter WHERE verified=0").fetchone()[0]
                metrics["zuchter_verified"] = conn.execute("SELECT COUNT(*) FROM wiki_zuchter WHERE verified=1").fetchone()[0]
            except Exception:
                metrics["zuchter_pending"] = metrics["zuchter_verified"] = 0

            # Community
            metrics["users"]          = conn.execute("SELECT COUNT(*) FROM users").fetchone()[0]
            metrics["dogs"]           = conn.execute("SELECT COUNT(*) FROM dogs").fetchone()[0]
            metrics["diary_entries"]  = conn.execute("SELECT COUNT(*) FROM diary").fetchone()[0]
            metrics["poison_active"]  = conn.execute("SELECT COUNT(*) FROM poison WHERE geloest=0").fetchone()[0]
            try:
                metrics["lost_active"] = conn.execute("SELECT COUNT(*) FROM lost WHERE gefunden=0").fetchone()[0]
            except Exception:
                metrics["lost_active"] = 0

            # Wiki-Interesse
            try:
                metrics["interesse_hat"]  = conn.execute("SELECT COUNT(*) FROM wiki_breed_interest WHERE typ='hat'").fetchone()[0]
                metrics["interesse_will"] = conn.execute("SELECT COUNT(*) FROM wiki_breed_interest WHERE typ='will'").fetchone()[0]
            except Exception:
                metrics["interesse_hat"] = metrics["interesse_will"] = 0
    except Exception as e:
        logger.error(f"Status-Report: DB-Fehler: {e}")
        return

    # --- Job-Log-Tabelle ---
    job_labels = {
        "health_reminders":     "Gesundheits-Erinnerungen",
        "poison_archive":       "Giftköder-Archiv",
        "weather_alert":        "Wetter-Alert",
        "milestone_check":      "Meilenstein-Check",
        "import_events":        "Event-Import (VDH)",
        "seed_breeds":         "Rassen-Seed (TheDogAPI, monatlich)",
        "seed_wikidata":       "Rassen-Seed (Wikidata, monatlich)",
        "weekly_praise":        "Wöchentlicher Lober (Mo 09:00)",
        "ki_health_report":     "KI-Gesundheitsberichte",
    }
    job_rows_html = ""
    job_rows_txt  = ""
    for jid, label in job_labels.items():
        log = _job_log.get(jid)
        if log:
            ts      = log["last_run"].strftime("%d.%m. %H:%M")
            status  = "✅" if log["status"] == "ok" else "❌"
            result  = log["result"]
            color   = "#16a34a" if log["status"] == "ok" else "#dc2626"
            job_rows_html += f'<tr><td style="padding:5px 10px;color:#555">{label}</td><td style="padding:5px 10px;font-family:monospace;font-size:12px">{ts}</td><td style="padding:5px 10px;color:{color}">{status} {result}</td></tr>'
            job_rows_txt  += f"  {status} {label}: {ts} — {result}\n"
        else:
            job_rows_html += f'<tr><td style="padding:5px 10px;color:#555">{label}</td><td style="padding:5px 10px;color:#aaa" colspan="2">— noch nicht gelaufen</td></tr>'
            job_rows_txt  += f"  — {label}: noch nicht gelaufen\n"

    html = f"""\
<!DOCTYPE html>
<html lang="de">
<head><meta charset="utf-8"></head>
<body style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#f5f0ea;margin:0;padding:0">
<div style="max-width:600px;margin:24px auto;background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 2px 16px rgba(0,0,0,.08)">

  <!-- Header -->
  <div style="background:linear-gradient(135deg,#C4843A,#e8a857);padding:24px 28px;color:#fff">
    <div style="font-size:22px;font-weight:800;margin-bottom:2px">🐾 Ban Yaro — Status-Report</div>
    <div style="opacity:.88;font-size:13px">{now_str} Uhr</div>
  </div>

  <!-- Scheduler-Status -->
  <div style="padding:20px 28px;border-bottom:1px solid #f0e8dc">
    <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#C4843A;margin-bottom:10px">Scheduler-Jobs</div>
    <table style="width:100%;border-collapse:collapse;font-size:13px">
      {job_rows_html}
    </table>
  </div>

  <!-- Community-Metriken -->
  <div style="padding:20px 28px;border-bottom:1px solid #f0e8dc">
    <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#C4843A;margin-bottom:10px">Community</div>
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
      {"".join(f'<div style="background:#fdf6ef;border-radius:8px;padding:10px 14px"><div style="font-size:20px;font-weight:800;color:#C4843A">{v}</div><div style="font-size:11px;color:#888">{k}</div></div>' for k,v in [
        ("Nutzer",metrics["users"]),
        ("Hunde",metrics["dogs"]),
        ("Tagebuch-Einträge",metrics["diary_entries"]),
        ("Aktive Giftköder",metrics["poison_active"]),
        ("Vermisste Hunde",metrics["lost_active"]),
        ("'So einen hab ich'",metrics["interesse_hat"]),
        ("'Interessiert mich'",metrics["interesse_will"]),
        ("Züchter (pending)",metrics["zuchter_pending"]),
      ])}
    </div>
  </div>

  <!-- Footer -->
  <div style="padding:14px 28px;background:#fdf6ef;font-size:11px;color:#aaa;text-align:center">
    Ban Yaro · banyaro.app · Nächster Report in ~6h
  </div>

</div>
</body>
</html>"""

    plain = f"""Ban Yaro Status-Report — {now_str}

=== Scheduler-Jobs ===
{job_rows_txt}
=== Community ===
Nutzer: {metrics['users']}
Hunde: {metrics['dogs']}
Tagebuch-Einträge: {metrics['diary_entries']}
Aktive Giftköder: {metrics['poison_active']}
Vermisste Hunde: {metrics['lost_active']}
'So einen hab ich': {metrics['interesse_hat']}
'Interessiert mich': {metrics['interesse_will']}
Züchter (pending): {metrics['zuchter_pending']}
"""

    try:
        await send_email(admin, f"Ban Yaro Status {now_str}", html, plain)
        logger.info(f"Status-Report gesendet an {admin}.")
    except Exception as e:
        logger.error(f"Status-Report: Mail-Fehler: {e}")


def _compute_milestone(today: date, bday: date, dog_name: str):
    """
    Gibt (titel, text) zurück wenn heute ein Meilenstein-Tag ist,
    sonst None.

    Regeln:
    - Jahrestag (Monat + Tag stimmen überein, Jahrgang ≥ 1):
      "🎂 <Name> ist X Jahr(e) alt!"
    - Monatsjubiläum in den ersten 11 Monaten (Geburtsmonats-Tag):
      "🐾 <Name> ist heute X Monat(e) alt!"
    """
    # Jahrestag?
    if today.month == bday.month and today.day == bday.day:
        years = today.year - bday.year
        if years <= 0:
            return None  # Geburtstag selbst (Tag 0) → kein Eintrag
        years_label = f"{years} Jahr" if years == 1 else f"{years} Jahre"
        titel = f"🎂 {dog_name} ist {years_label} alt!"
        text  = (
            f"Heute feiern wir {dog_name}s {years}. Geburtstag! 🐾🎉 "
            f"Herzlichen Glückwunsch zum {years_label}!"
        )
        return titel, text

    # Monatsjubiläum (nur innerhalb des ersten Lebensjahres)?
    # today liegt im selben Monatstag wie der Geburtstag aber in einem anderen Monat.
    if today.day == bday.day:
        # Vollständige Monate seit Geburt berechnen
        months = (today.year - bday.year) * 12 + (today.month - bday.month)
        if 1 <= months <= 11:
            months_label = f"{months} Monat" if months == 1 else f"{months} Monate"
            titel = f"🐾 {dog_name} ist heute {months_label} alt!"
            text  = (
                f"{dog_name} wird heute {months_label} alt — "
                f"was für ein tolles kleines Hundeleben! 🥳"
            )
            return titel, text

    return None
