"""Background jobs.

Run one job (cPanel cron):
    python scripts/jobs.py tron
    python scripts/jobs.py expire
    python scripts/jobs.py process
    python scripts/jobs.py all

Or run them continuously in one process (systemd / `nohup`):
    python scripts/jobs.py loop
"""
import asyncio
import sys
from pathlib import Path

sys.path.append(str(Path(__file__).resolve().parents[1]))

from app.core.database import SessionFactory  # noqa: E402
from app.core.logging import get_logger, setup_logging  # noqa: E402
from app.models import OrderStatus  # noqa: E402
from app.repositories import OrderRepository  # noqa: E402
from app.services import OrderService, PaymentMonitor, PaymentService  # noqa: E402

logger = get_logger("app.jobs")

INTERVALS = {"tron": 60, "expire": 300, "process": 120}


async def _run(name: str) -> None:
    async with SessionFactory() as session:
        try:
            if name == "tron":
                matched = await PaymentMonitor(session).check_tron_payments()
                if matched:
                    logger.info("tron payments matched: %s", matched)
            elif name == "expire":
                expired = await PaymentService(session).expire_pending()
                if expired:
                    logger.info("invoices expired: %s", expired)
            elif name == "process":
                orders = OrderRepository(session)
                service = OrderService(session)
                pending = await orders.list_by_status([OrderStatus.PAID], limit=20)
                for order in pending:
                    await service.process(order)
                if pending:
                    logger.info("orders processed: %s", len(pending))
            await session.commit()
        except Exception as exc:  # noqa: BLE001 - a failing job must not kill the loop
            await session.rollback()
            logger.exception("job %s failed: %s", name, exc)


async def run_job(name: str) -> None:
    if name == "all":
        for job in INTERVALS:
            await _run(job)
        return
    await _run(name)


async def loop() -> None:
    counters = {name: 0 for name in INTERVALS}
    logger.info("job loop started")
    while True:
        for name, interval in INTERVALS.items():
            counters[name] += 10
            if counters[name] >= interval:
                counters[name] = 0
                await _run(name)
        await asyncio.sleep(10)


if __name__ == "__main__":
    setup_logging()
    command = sys.argv[1] if len(sys.argv) > 1 else "all"
    if command == "loop":
        asyncio.run(loop())
    elif command in {*INTERVALS, "all"}:
        asyncio.run(run_job(command))
    else:
        print(__doc__)
        sys.exit(1)
