"""Runs the whole catalogue pipeline as one job.

A merchant presses one button. Making the frontend call three endpoints, poll
three jobs, and decide what to do when the second fails puts backend
orchestration in the UI -- so the sequence lives here and reports as a single
0-100.

The three stages stay individually callable for anyone who wants finer control.
"""
import logging

from app.services.catalog.sources import get_sources
from app.services.enrichment.job import enrich_tenant
from app.services.pairing.job import pair_tenant

logger = logging.getLogger(__name__)

# Weighted by measured duration on a 218-product catalogue: sync is seconds,
# enrichment about three and a half minutes, pairing about ninety seconds. A bar
# that spends most of its life in the shortest stage is worse than no bar.
STAGES = (("sync", 0, 10), ("enrich", 10, 60), ("pair", 60, 100))


def _scaled(progress, start: int, end: int):
    """Maps a stage's own 0-100 into its slice of the overall bar."""
    if progress is None:
        return None

    def report(percent: int, step: str) -> None:
        progress(start + (end - start) * int(percent) // 100, step)

    return report


def build_catalog(tenant_id: str, progress=None, force: bool = False) -> dict:
    """Sync, enrich and pair, in that order, as one unit of work.

    Ordering is not a preference: pairing reads the attributes enrichment
    writes. is_accessory is what makes a phone suggest a case and stops a case
    suggesting a phone, so pairing an un-enriched catalogue produces a graph
    with no complements worth having.
    """
    report = {"stages": {}}

    sources = get_sources(tenant_id)
    if sources:
        # Imported here to avoid a circular import: the API module that owns
        # the sync wrapper imports this one.
        from app.api.endpoints import _sync_sources

        if progress:
            progress(0, f"syncing {len(sources)} source(s)")
        report["stages"]["sync"] = _sync_sources(sources, force=force)(
            tenant_id, progress=_scaled(progress, *STAGES[0][1:]))
    else:
        # A CSV-only tenant has nothing to fetch. Failing here would break the
        # one button this exists for.
        logger.info("No connected sources for %s; skipping sync", tenant_id)
        report["stages"]["sync"] = {"skipped": "no connected sources"}
        if progress:
            progress(10, "no sources to sync")

    report["stages"]["enrich"] = enrich_tenant(
        tenant_id, force=force, progress=_scaled(progress, *STAGES[1][1:]))

    report["stages"]["pair"] = pair_tenant(
        tenant_id, progress=_scaled(progress, *STAGES[2][1:]))

    if progress:
        progress(100, "catalogue ready")

    pairs = report["stages"]["pair"]
    report["products"] = pairs.get("products", 0)
    report["pairs"] = pairs.get("pairs", 0)
    logger.info("Built catalogue for %s: %s products, %s pairs",
                tenant_id, report["products"], report["pairs"])
    return report
