"""Watcher d'alertes de prix → email.

Boucle toutes les WATCH_INTERVAL secondes : lit `price_alerts.yaml` (workspace), récupère le
cours live de chaque ticker, et envoie un email quand un seuil est franchi. L'email part vers le
**relais postfix du lab** (storage-01:25) qui réécrit l'expéditeur en Gmail — aucun secret ici
(les pods k8s sont dans `mynetworks`).

État dans `.watcher-state.json` (workspace) : un seuil franchi ne renvoie qu'**un** mail (pas de
spam). Pour ré-armer une règle, on la ré-édite (ou on vide l'état).

Usage :
  python -m finlab.watcher            # boucle (conteneur sidecar)
  python -m finlab.watcher --once     # une seule évaluation puis sort
  python -m finlab.watcher --test x@y # envoie un email de test et sort
"""
from __future__ import annotations

import json
import os
import smtplib
import sys
import time
from email.message import EmailMessage
from pathlib import Path

import yaml

from . import data

HOME = Path(os.environ.get("FINLAB_HOME") or Path(__file__).resolve().parent.parent)
ALERTS_FILE = HOME / "price_alerts.yaml"
STATE_FILE = HOME / ".watcher-state.json"

INTERVAL = int(os.environ.get("WATCH_INTERVAL", "300"))
SMTP_HOST = os.environ.get("SMTP_HOST", "192.168.10.1")
SMTP_PORT = int(os.environ.get("SMTP_PORT", "25"))
SENDER = os.environ.get("SMTP_SENDER", "finlab@funk.lab")  # réécrit par le relais postfix


def _log(msg: str) -> None:
    print(f"[watcher] {msg}", flush=True)


def _load_state() -> set[str]:
    try:
        return set(json.load(open(STATE_FILE)))
    except Exception:
        return set()


def _save_state(s: set[str]) -> None:
    try:
        json.dump(sorted(s), open(STATE_FILE, "w"))
    except Exception as e:
        _log(f"état non sauvegardé: {e}")


def send_email(to: str, subject: str, body: str) -> None:
    msg = EmailMessage()
    msg["From"] = SENDER
    msg["To"] = to
    msg["Subject"] = subject
    msg.set_content(body)
    with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=20) as s:
        s.send_message(msg)


def _rule_key(r: dict) -> str:
    return f"{r.get('ticker')}|{r.get('above')}|{r.get('below')}"


def check_once() -> None:
    """Évalue toutes les règles une fois ; envoie les emails dus."""
    if not ALERTS_FILE.exists():
        return
    cfg = yaml.safe_load(open(ALERTS_FILE, encoding="utf-8")) or {}
    rules = cfg.get("rules") or []
    to = cfg.get("email_to")
    if not to or not rules:
        return

    state = _load_state()
    valid_keys = {_rule_key(r) for r in rules}
    fired = 0
    for r in rules:
        key = _rule_key(r)
        if key in state:
            continue
        try:
            price, cur = data.last_price(r["ticker"], fresh=True)
        except Exception as e:
            _log(f"{r.get('ticker')}: cours indisponible ({e})")
            continue
        hit = None
        if r.get("above") is not None and price >= r["above"]:
            hit = f"≥ {r['above']}"
        elif r.get("below") is not None and price <= r["below"]:
            hit = f"≤ {r['below']}"
        if not hit:
            continue
        note = f" — {r['note']}" if r.get("note") else ""
        try:
            send_email(
                to,
                f"🔔 FinLab : {r['ticker']} à {price:.2f} {cur} (seuil {hit}){note}",
                f"{r['ticker']} a atteint {price:.2f} {cur} (seuil {hit}).{note}\n\n"
                f"Alerte de prix configurée dans price_alerts.yaml.\n— FinLab",
            )
            state.add(key)
            fired += 1
            _log(f"ALERTE envoyée: {r['ticker']} {price:.2f} {cur} ({hit}) → {to}")
        except Exception as e:
            _log(f"échec envoi {r.get('ticker')}: {e}")
    # purge les états de règles supprimées (ré-armement propre)
    state &= valid_keys
    _save_state(state)
    if fired:
        _log(f"{fired} alerte(s) envoyée(s).")


def main() -> None:
    args = sys.argv[1:]
    if args and args[0] == "--test":
        to = args[1] if len(args) > 1 else os.environ.get("ALERT_EMAIL", "")
        send_email(to, "✅ FinLab — email de test", "Le relais email fonctionne. — FinLab")
        _log(f"email de test envoyé à {to}")
        return
    if args and args[0] == "--once":
        check_once()
        return
    _log(f"démarrage — intervalle {INTERVAL}s, relais {SMTP_HOST}:{SMTP_PORT}, règles: {ALERTS_FILE}")
    while True:
        try:
            check_once()
        except Exception as e:
            _log(f"erreur boucle: {e}")
        time.sleep(INTERVAL)


if __name__ == "__main__":
    main()
