"""Publish durable project-job intents from DB outbox → Redis ARQ."""

from __future__ import annotations

import structlog

from src.domain.projects.job_outbox_constants import (
    JOB_KIND_CLOUD_SYNC_INITIAL,
    JOB_KIND_CLOUD_SYNC_POST_READY,
    JOB_KIND_PROJECT_SETUP,
    STALE_PUBLISHING_SECONDS,
)

logger = structlog.get_logger()


async def dispatch_project_outbox(ctx: dict) -> dict:
    from src.core.database import async_session_factory
    from src.domain.projects.repository import ProjectJobOutboxRepository
    from src.workers.enqueue import enqueue_cloud_sync_project, enqueue_project_setup

    db_factory = ctx.get("db_factory", async_session_factory)
    stats = {"claimed": 0, "published": 0, "requeued_or_failed": 0}

    async with db_factory() as db:
        repo = ProjectJobOutboxRepository(db)
        reset_n = await repo.reset_stale_publishing_locks(stale_seconds=STALE_PUBLISHING_SECONDS)
        claimed = await repo.claim_batch_for_publishing(limit=40)
        await db.commit()

    if reset_n:
        logger.info("project_outbox_stale_publish_reset", count=reset_n)

    stats["claimed"] = len(claimed)

    for row in claimed:
        ok = False
        err_msg = ""
        try:
            if row.job_kind == JOB_KIND_PROJECT_SETUP:
                ok = await enqueue_project_setup(str(row.project_id))
                if not ok:
                    err_msg = "enqueue_project_setup_skipped_or_false"
            elif row.job_kind in (JOB_KIND_CLOUD_SYNC_INITIAL, JOB_KIND_CLOUD_SYNC_POST_READY):
                ok = await enqueue_cloud_sync_project(str(row.project_id))
                if not ok:
                    err_msg = "enqueue_cloud_sync_skipped_or_false"
            elif row.job_kind in ("discovery_turn", "discovery_apply"):
                from src.workers.enqueue import enqueue_discovery_job
                ok = await enqueue_discovery_job(row.job_kind, str(row.project_id), int(row.payload_json["version"]))
            else:
                err_msg = f"unknown_job_kind:{row.job_kind}"
        except Exception as exc:
            err_msg = str(exc)
            ok = False

        async with db_factory() as db:
            repo = ProjectJobOutboxRepository(db)
            if ok:
                await repo.mark_published(row.id)
                stats["published"] += 1
            else:
                await repo.mark_enqueue_failed(row.id, err_msg or "enqueue_failed")
                stats["requeued_or_failed"] += 1
            await db.commit()

    if stats["claimed"]:
        logger.info("dispatch_project_outbox_tick", **stats)
    return stats
