"""وظایف Celery.

در فاز ۱ فقط یک وظیفه نمونه پیاده شده است: process_outbox_events که
رویدادهای pending را می‌خواند و وضعیت آن‌ها را به processed/failed تغییر
می‌دهد. انتشار واقعی رویدادها (به message bus یا webhook) در فازهای بعد.
"""
from __future__ import annotations

import asyncio
from datetime import datetime, timezone

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

from app.core.config import settings
from app.core.logging import get_logger
from app.repositories.registry import OutboxRepository
from app.workers.celery_app import celery_app
from shared.events import EventStatus

logger = get_logger(__name__)


async def process_pending_outbox(session: AsyncSession, *, batch_size: int = 100) -> dict:
    """منطق اصلی پردازش Outbox روی یک session مشخص.

    این تابع مستقل از Celery است تا هم توسط task و هم در تست قابل استفاده باشد.
    برمی‌گرداند: تعداد پردازش‌شده و ناموفق.
    """
    repo = OutboxRepository(session)
    pending = await repo.list_pending(limit=batch_size)

    processed = 0
    failed = 0
    now = datetime.now(timezone.utc)

    for event in pending:
        try:
            # در فاز ۱ صرفاً به‌عنوان پردازش‌شده علامت می‌خورد.
            # منطق انتشار واقعی رویداد در فازهای بعدی اینجا اضافه می‌شود.
            event.status = EventStatus.PROCESSED
            event.processed_at = now
            processed += 1
        except Exception as exc:  # pragma: no cover - محافظ
            event.status = EventStatus.FAILED
            event.retry_count += 1
            failed += 1
            logger.warning(
                "outbox_event_failed",
                extra={"event_id": str(event.id), "error": str(exc)},
            )

    await session.commit()
    return {"processed": processed, "failed": failed}


async def _run_process_outbox() -> dict:
    """ساخت engine/session مستقل برای اجرای task در حلقه رویداد جدید."""
    engine = create_async_engine(settings.database_url, pool_pre_ping=True)
    session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
    try:
        async with session_factory() as session:
            return await process_pending_outbox(session)
    finally:
        await engine.dispose()


@celery_app.task(name="app.workers.tasks.process_outbox_events")
def process_outbox_events() -> dict:
    """Task دوره‌ای پردازش رویدادهای Outbox."""
    result = asyncio.run(_run_process_outbox())
    logger.info("outbox_processed", extra=result)
    return result


def _run_remote_ssl_provision(domain: str) -> str:
    """اجرای اسکریپت provision_ssl روی سرور nginx از طریق SSH."""
    import base64
    import shlex

    import paramiko

    host = settings.ssl_provision_host
    user = settings.ssl_provision_user
    password = settings.ssl_provision_password
    script = settings.ssl_provision_script
    if not host or not user or not password:
        raise RuntimeError("SSL provision SSH credentials are not configured")

    inner = f"python3 {shlex.quote(script)} {shlex.quote(domain)}"
    b64 = base64.b64encode(inner.encode("utf-8")).decode("ascii")
    remote = (
        f"echo {shlex.quote(password)} | sudo -S -p '' "
        f"bash -c {shlex.quote(f'echo {b64} | base64 -d | bash')}"
    )

    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        client.connect(
            host,
            username=user,
            password=password,
            timeout=30,
            allow_agent=False,
            look_for_keys=False,
        )
        _, stdout, stderr = client.exec_command(remote, timeout=300, get_pty=True)
        out = (stdout.read() + stderr.read()).decode("utf-8", "replace")
        exit_status = stdout.channel.recv_exit_status()
        if exit_status != 0:
            raise RuntimeError(f"ssl provision failed ({exit_status}): {out[-2000:]}")
        return out
    finally:
        client.close()


@celery_app.task(
    name="app.workers.tasks.provision_domain_ssl",
    bind=True,
    max_retries=5,
    default_retry_delay=60,
)
def provision_domain_ssl(self, domain: str) -> dict:
    """صدور خودکار SSL برای دامنهٔ tenant تازه‌ساخته‌شده."""
    if not settings.ssl_provision_enabled:
        return {"skipped": True, "domain": domain, "reason": "disabled"}
    try:
        output = _run_remote_ssl_provision(domain)
        logger.info("ssl_provision_ok", extra={"domain": domain})
        return {"ok": True, "domain": domain, "output": output[-1500:]}
    except Exception as exc:
        logger.warning(
            "ssl_provision_retry",
            extra={"domain": domain, "error": str(exc), "retries": self.request.retries},
        )
        raise self.retry(exc=exc)
