#!/usr/bin/env python3
"""
Wetterserver-Monitor: Prüft alle 5 Minuten ob Wetterdaten ankommen.
- Alert-E-Mail wenn >15 Minuten keine Daten (Wiederholung nach 2h)
- Tägliche Status-E-Mail zur Bestätigung dass alles läuft

Cron-Empfehlung: alle 5 Minuten
  */5 * * * * /pfad/zu/.venv/bin/python /pfad/zu/check_wetterserver.py >> /pfad/zu/monitor.log 2>&1
"""

import json
import os
import smtplib
import ssl
import urllib.request
from datetime import datetime, timezone, timedelta, date
from email.message import EmailMessage
from pathlib import Path
from typing import Optional

SCRIPT_DIR = Path(__file__).parent
ENV_FILE = SCRIPT_DIR / ".env"
STATE_FILE = SCRIPT_DIR / ".monitoring_state.json"

API_URL = "https://stwwetter.fuerst-stuttgart.de/api/weather/latest"
TIMEOUT_MINUTES = 15
RESEND_AFTER_HOURS = 2


def _ssl_context() -> ssl.SSLContext:
    try:
        import certifi
        return ssl.create_default_context(cafile=certifi.where())
    except ImportError:
        return ssl.create_default_context()


def load_env() -> None:
    if not ENV_FILE.exists():
        return
    for line in ENV_FILE.read_text().splitlines():
        line = line.strip()
        if line and not line.startswith("#") and "=" in line:
            key, _, val = line.partition("=")
            val = val.strip().strip("'\"")
            if key not in os.environ:
                os.environ[key] = val


def load_state() -> dict:
    if STATE_FILE.exists():
        try:
            return json.loads(STATE_FILE.read_text())
        except Exception:
            pass
    return {"alert_active": False, "last_alert_sent": None, "last_daily_report": None}


def save_state(state: dict) -> None:
    STATE_FILE.write_text(json.dumps(state, indent=2))


def get_latest_weather() -> Optional[dict]:
    try:
        with urllib.request.urlopen(API_URL, timeout=10, context=_ssl_context()) as resp:
            return json.loads(resp.read())
    except Exception as e:
        print(f"API-Anfrage fehlgeschlagen: {e}")
        return None


def parse_datetime(dt_str: str) -> Optional[datetime]:
    try:
        dt = datetime.fromisoformat(str(dt_str).replace("Z", "+00:00"))
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return dt
    except Exception:
        return None


def _send_email(subject: str, body: str) -> bool:
    smtp_host = os.getenv("MONITOR_SMTP_HOST", "smtp.gmx.de")
    smtp_port = int(os.getenv("MONITOR_SMTP_PORT", "587"))
    smtp_user = os.getenv("MONITOR_SMTP_USER", "")
    smtp_pass = os.getenv("MONITOR_SMTP_PASSWORD", "")
    to_email = os.getenv("MONITOR_TO_EMAIL", "rxf@gmx.de")
    from_email = os.getenv("MONITOR_FROM_EMAIL", smtp_user)

    if not smtp_user or not smtp_pass:
        print("FEHLER: MONITOR_SMTP_USER / MONITOR_SMTP_PASSWORD nicht in .env gesetzt")
        return False

    msg = EmailMessage()
    msg["Subject"] = subject
    msg["From"] = from_email
    msg["To"] = to_email
    msg.set_content(body)

    try:
        with smtplib.SMTP(smtp_host, smtp_port) as smtp:
            smtp.ehlo()
            smtp.starttls()
            smtp.login(smtp_user, smtp_pass)
            smtp.send_message(msg)
        print(f"E-Mail gesendet: {subject!r} -> {to_email}")
        return True
    except Exception as e:
        print(f"FEHLER beim E-Mail-Versand: {e}")
        return False


def send_alert(latest_dt: Optional[datetime], state: dict, now: datetime) -> None:
    if latest_dt is not None:
        age_min = int((now - latest_dt).total_seconds() / 60)
        body = (
            f"Achtung: Die Wetterstation sendet seit {age_min} Minuten keine Daten mehr.\n\n"
            f"Letzter Datenpunkt: {latest_dt.strftime('%d.%m.%Y %H:%M:%S UTC')}\n"
            f"Aktueller Zeitpunkt: {now.strftime('%d.%m.%Y %H:%M:%S UTC')}\n\n"
            f"Bitte überprüfen Sie den Wetterserver.\n"
            f"API: {API_URL}"
        )
    else:
        body = (
            f"Achtung: Die Wetterstation-API ist nicht erreichbar oder liefert keine Daten.\n\n"
            f"Zeitpunkt: {now.strftime('%d.%m.%Y %H:%M:%S UTC')}\n\n"
            f"Bitte überprüfen Sie den Wetterserver.\n"
            f"API: {API_URL}"
        )
    if _send_email("⚠ Wetterstation: Keine Daten seit >15 Minuten", body):
        state["last_alert_sent"] = now.isoformat()


def send_daily_report(data: dict, latest_dt: datetime, now: datetime, state: dict) -> None:
    temp = data.get("temperature")
    humidity = data.get("humidity")
    pressure = data.get("pressure")
    age_min = int((now - latest_dt).total_seconds() / 60)

    lines = [
        "✓ Die Wetterstation läuft einwandfrei.",
        "",
        f"Letzter Datenpunkt: {latest_dt.strftime('%d.%m.%Y %H:%M:%S UTC')} (vor {age_min} Min.)",
        f"Prüfzeitpunkt:      {now.strftime('%d.%m.%Y %H:%M:%S UTC')}",
        "",
        "Aktuelle Messwerte:",
    ]
    if temp is not None:
        lines.append(f"  Temperatur:  {temp:.1f} °C")
    if humidity is not None:
        lines.append(f"  Luftfeuchte: {humidity} %")
    if pressure is not None:
        lines.append(f"  Luftdruck:   {pressure:.1f} hPa")
    lines += ["", f"API: {API_URL}"]

    if _send_email("✓ Wetterstation: Täglicher Status – alles in Ordnung", "\n".join(lines)):
        state["last_daily_report"] = date.today().isoformat()


def main() -> None:
    load_env()
    state = load_state()
    now = datetime.now(timezone.utc)

    weather = get_latest_weather()
    latest_dt: Optional[datetime] = None
    if weather:
        dt_str = weather.get("datetime") or weather.get("received_at")
        if dt_str:
            latest_dt = parse_datetime(dt_str)

    outage = latest_dt is None or (now - latest_dt) > timedelta(minutes=TIMEOUT_MINUTES)

    if outage:
        should_send = not state["alert_active"]
        if not should_send and state.get("last_alert_sent"):
            last_sent = datetime.fromisoformat(state["last_alert_sent"])
            should_send = (now - last_sent) > timedelta(hours=RESEND_AFTER_HOURS)

        state["alert_active"] = True
        if should_send:
            send_alert(latest_dt, state, now)
        else:
            print(
                f"{now.strftime('%Y-%m-%d %H:%M:%S')} Ausfall aktiv, "
                f"nächste E-Mail frühestens um "
                f"{(datetime.fromisoformat(state['last_alert_sent']) + timedelta(hours=RESEND_AFTER_HOURS)).strftime('%H:%M')} UTC"
            )
    else:
        if state["alert_active"]:
            print(
                f"{now.strftime('%Y-%m-%d %H:%M:%S')} Wetterstation liefert wieder Daten "
                f"(letzter Datenpunkt: {latest_dt.strftime('%d.%m.%Y %H:%M:%S UTC')})"
            )
        state["alert_active"] = False

        today = date.today().isoformat()
        if state.get("last_daily_report") != today:
            send_daily_report(weather, latest_dt, now, state)
        else:
            print(
                f"{now.strftime('%Y-%m-%d %H:%M:%S')} OK – "
                f"letzter Datenpunkt: {latest_dt.strftime('%H:%M:%S UTC')}"
            )

    save_state(state)


if __name__ == "__main__":
    main()
