"""Ütemezett feladatok segédei: EGYSZERI FUTÁS biztosítása több gunicorn worker mellett.

TÖRTÉNET / MIÉRT KÉSZÜLT (2026-10-04, e-mail rendszer B szelet)
    Az éles szerver 3 gunicorn workerrel fut. A main.py modulszinten indítja az APScheduler-t,
    ezért MINDEN worker elindítja a saját ütemezőjét - a napi riport (06:00) és a korlátozott gépek
    ellenőrzése (06:05) így akár 3-szor is lefuthatott, és ugyanaz a levél 3-szor ment ki.

HOGYAN OLDJUK MEG?
    Minden futás előtt a feladat megpróbál beszúrni egy (feladat, kulcs) párt az
    `utemezett_futasok` táblába (models.UtemezettFutas). A pár egyedi, ezért az adatbázis
    pontosan EGY workernek engedi a beszúrást - a többi IntegrityError-t kap és kihagyja a futást.
        - napi feladatnál a kulcs a helyi dátum   ("2026-10-05"),
        - percenkénti feladatnál az UTC perc         ("2026-10-05T04:01").
    Előnyök a fájlzárral szemben: működik Windows-on (fejlesztés) és Linuxon is, nem akad meg, ha
    egy worker összeomlik, és nincs szükség külső szolgáltatásra (Redis).

MIT NE TEGYÉL?
    - Ne hívd közvetlenül a védett feladatot az ütemezőből: használd a `vedett()` burkolót.
    - A kézi indítás (pl. /teszt-riport) NEM megy át a védelmen - az szándékosan azonnal fut.

Részletes leírás: docs/EMAIL_RENDSZER.md
"""
from datetime import datetime, timedelta, timezone
from typing import Callable

from sqlalchemy.exc import IntegrityError

import models
from database import SessionLocal
from templates_env import HELYI_TZ


def egyszeri_futas(feladat: str, kulcs: str) -> bool:
    """Igaz, ha EZ a folyamat nyerte meg a (feladat, kulcs) futást, és tehát le kell futtatnia.
    Hamis, ha egy másik folyamat már lefoglalta, VAGY ha az adatbázis hibát adott (utóbbi esetben
    inkább kihagyjuk a futást, mintsem hogy többszörösen fusson - a hibát a konzolra naplózzuk)."""
    db = SessionLocal()
    try:
        db.add(models.UtemezettFutas(feladat=feladat, kulcs=kulcs))
        db.commit()
        return True
    except IntegrityError:
        db.rollback()
        return False
    except Exception as e:
        db.rollback()
        print(f"❌ Ütemezett futás-védelem hiba ({feladat}/{kulcs}): {type(e).__name__}: {e}")
        return False
    finally:
        db.close()


def _napi_kulcs() -> str:
    return datetime.now(HELYI_TZ).strftime("%Y-%m-%d")


def _perc_kulcs() -> str:
    # UTC (nem helyi) idő: az őszi óraátállításkor a helyi 02:00-02:59 óra KÉTSZER létezik, a második
    # előfordulás percenkénti kulcsa ütközne az elsőével, és az egyszeri-futás védelem egy órára leállítaná
    # a percenkénti feladatot. Az UTC percek egyediek.
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M")


def vedett(feladat: str, fuggveny: Callable[[], None], percenkenti: bool = False) -> Callable[[], None]:
    """Becsomagolja az ütemezett függvényt: csak akkor hívja meg, ha ez a folyamat nyerte a
    futást. `percenkenti=True` esetén a kulcs a perc, különben a nap."""
    def _burkolo():
        kulcs = _perc_kulcs() if percenkenti else _napi_kulcs()
        if egyszeri_futas(feladat, kulcs):
            fuggveny()
    _burkolo.__name__ = f"vedett_{feladat}"
    return _burkolo


def takaritas(megtartas_nap: int = 7) -> int:
    """Törli a régi futás-bejegyzéseket (naponta egyszer hívjuk az email_sor.takaritas-ból).
    Visszatérés: a törölt sorok száma."""
    db = SessionLocal()
    try:
        hatar = datetime.now(timezone.utc).replace(tzinfo=None) - timedelta(days=megtartas_nap)  # UTC, tz nélkül (az oszlop is így tárolja)
        torolt = db.query(models.UtemezettFutas).filter(models.UtemezettFutas.futott_at < hatar).delete()
        db.commit()
        return torolt
    except Exception as e:
        db.rollback()
        print(f"❌ Ütemezett futás-bejegyzések takarítása sikertelen: {type(e).__name__}: {e}")
        return 0
    finally:
        db.close()
