"""Shortest Magento outbound path after master taxonomy is corrected.

1. Optionally repair live category_links to expected L3 + collection + all-products
2. Enqueue products-only force upsert (categories + shopping_* attrs)

CLI:
    # Preview expected category repair + queue dry-run
    python -m app.jobs.push_magento_taxonomy_outbound --connection-id 1 --sku ACH-GDW1530

    # Kitchen Cabinets hub (all SKUs on kitchen-cabinets/* listing paths)
    python -m app.jobs.push_magento_taxonomy_outbound --connection-id 1 \\
        --path-prefix kitchen-cabinets --apply --wait --run-worker

    # Explicit SKUs, skip link repair (product upsert only)
    python -m app.jobs.push_magento_taxonomy_outbound --connection-id 1 \\
        --sku ACH-B12 --sku ACH-GDW1530 --apply --no-repair-links --wait --run-worker
"""

from __future__ import annotations

import argparse
import json
import logging
from datetime import datetime
from typing import Any, Dict, List, Optional

from sqlalchemy import select

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def resolve_skus_for_path_prefix(session, *, path_prefix: str, limit: Optional[int] = None) -> List[str]:
    from channel.url_canonical import normalize_plp_path
    from db.models import ProductListingPathAssignment, ChannelListingPath

    prefix = normalize_plp_path(path_prefix)
    if not prefix:
        return []
    stmt = (
        select(ProductListingPathAssignment.master_sku)
        .join(ChannelListingPath, ChannelListingPath.id == ProductListingPathAssignment.listing_path_id)
        .where(ChannelListingPath.is_active.is_(True))
        .where(ProductListingPathAssignment.assignment_status == "active")
        .where(
            (ChannelListingPath.path_slug == prefix)
            | (ChannelListingPath.path_slug.like(f"{prefix}/%"))
        )
        .distinct()
        .order_by(ProductListingPathAssignment.master_sku)
    )
    skus = [str(s).strip() for s in session.scalars(stmt).all() if str(s).strip()]
    if limit is not None:
        skus = skus[: max(0, int(limit))]
    return skus


def resolve_skus_for_collection(session, *, collection_path_slug: str) -> List[str]:
    from channel.url_canonical import normalize_plp_path
    from db.collection_landing_pages import get_collection_landing_page
    from db.collection_sku_mapping import resolve_collection_skus

    slug = normalize_plp_path(collection_path_slug)
    row = get_collection_landing_page(session, slug)
    if not row:
        raise ValueError(f"Collection not found: {slug}")
    resolved = resolve_collection_skus(
        session,
        path_slug=slug,
        category_l1=row.get("category_l1"),
        collection=row.get("collection"),
        sku_prefixes=row.get("sku_prefixes"),
    )
    return sorted(
        {
            str(item.get("master_sku") or "").strip()
            for item in (resolved.get("skus") or [])
            if str(item.get("master_sku") or "").strip()
        }
    )


def enqueue_products_force(
    session,
    *,
    native_connection_id: int,
    skus: List[str],
    dry_run: bool,
) -> Dict[str, Any]:
    from db.magento_repositories import SqlAlchemyMagentoSyncQueueRepository

    label = f"taxonomy-outbound-magento-{datetime.utcnow().strftime('%Y%m%d-%H%M%S')}"
    options: Dict[str, Any] = {
        "dry_run": dry_run,
        "use_master_catalog": True,
        "products_only": True,
        "force_upsert": True,
        "force_upsert_skus": list(skus),
        "limit_skus": list(skus),
        "force_relations": False,
        "run_pull_first": False,
        "run_pull_after": False,
    }
    queue_id = SqlAlchemyMagentoSyncQueueRepository(session).enqueue(
        native_connection_id,
        label,
        options=options,
        supersede_queued=True,
    )
    return {"queue_id": queue_id, "label": label, "options": options, "sku_count": len(skus)}


def run_push_magento_taxonomy_outbound(
    *,
    connection_id: int,
    skus: Optional[List[str]] = None,
    path_prefix: Optional[str] = None,
    collection_path_slug: Optional[str] = None,
    limit: Optional[int] = None,
    apply: bool = False,
    repair_links: bool = True,
    wait: bool = False,
    run_worker: bool = False,
    wait_timeout: int = 3600,
) -> Dict[str, Any]:
    """Enqueue Magento products-only force upsert after master taxonomy correction."""
    from db.compat_connections import compat_connection_id, decode_compat_connection_id
    from db.models import MagentoConnection
    from db.session import get_session
    from magento.sku_policy import filter_magento_push_skus

    dry_run = not apply
    wanted = [str(s).strip() for s in (skus or []) if str(s).strip()]

    with get_session() as session:
        channel_type, native_id = decode_compat_connection_id(connection_id)
        if channel_type != "magento":
            # Allow raw native Magento connection ids.
            conn = session.get(MagentoConnection, connection_id)
            if conn is None:
                return {"status": "failed", "error": f"Magento connection {connection_id} not found"}
            native_id = int(connection_id)
            channel_connection_id = compat_connection_id("magento", native_id)
        else:
            native_id = int(native_id)
            channel_connection_id = int(connection_id)
            conn = session.get(MagentoConnection, native_id)
            if conn is None:
                return {"status": "failed", "error": f"Magento connection {native_id} not found"}

        if wanted:
            if limit is not None:
                wanted = wanted[: max(0, int(limit))]
        elif collection_path_slug:
            wanted = resolve_skus_for_collection(session, collection_path_slug=collection_path_slug)
            if limit is not None:
                wanted = wanted[: max(0, int(limit))]
        elif path_prefix:
            wanted = resolve_skus_for_path_prefix(session, path_prefix=path_prefix, limit=limit)
        else:
            wanted = []

        wanted = filter_magento_push_skus(wanted)
        if not wanted:
            return {
                "status": "skipped",
                "reason": "no_pushable_skus",
                "connection_id": channel_connection_id,
                "native_connection_id": native_id,
            }

        summary: Dict[str, Any] = {
            "status": "ok",
            "dry_run": dry_run,
            "connection_id": channel_connection_id,
            "native_connection_id": native_id,
            "sku_count": len(wanted),
            "skus_sample": wanted[:20],
            "repair_links": repair_links,
        }

        if repair_links:
            from app.jobs.repair_magento_product_category_links import run_repair

            summary["category_link_repair"] = run_repair(
                connection_id=native_id,
                skus=wanted,
                dry_run=dry_run,
                remove_stale=True,
                add_missing=True,
            )

        enqueued = enqueue_products_force(
            session,
            native_connection_id=native_id,
            skus=wanted,
            dry_run=dry_run,
        )
        session.commit()
        summary["enqueue"] = enqueued

    if wait and enqueued.get("queue_id"):
        from app.jobs.push_channel_sample import _wait_for_magento_queue

        summary["wait"] = _wait_for_magento_queue(
            int(enqueued["queue_id"]),
            timeout=wait_timeout,
            run_worker=run_worker,
        )
        summary["status"] = summary["wait"].get("status") or summary["status"]

    return summary


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Fast Magento outbound push after master taxonomy correction "
        "(category link repair + products-only force upsert)."
    )
    parser.add_argument("--connection-id", type=int, required=True)
    parser.add_argument("--sku", action="append", dest="skus", default=[])
    parser.add_argument(
        "--path-prefix",
        default=None,
        help="Active listing-path prefix (e.g. kitchen-cabinets) to select SKUs",
    )
    parser.add_argument(
        "--collection-path",
        default=None,
        help="Collection landing path slug (e.g. kitchen-cabinets/anna-caramel-harvest)",
    )
    parser.add_argument("--limit", type=int, default=None)
    parser.add_argument("--apply", action="store_true", help="Write Magento + enqueue live push")
    parser.add_argument(
        "--no-repair-links",
        action="store_true",
        help="Skip live category_links repair (enqueue product upsert only)",
    )
    parser.add_argument("--wait", action="store_true")
    parser.add_argument("--run-worker", action="store_true")
    parser.add_argument("--wait-timeout", type=int, default=21600)
    args = parser.parse_args()

    if not args.skus and not args.path_prefix and not args.collection_path:
        parser.error("Provide --sku, --path-prefix, and/or --collection-path")

    result = run_push_magento_taxonomy_outbound(
        connection_id=args.connection_id,
        skus=args.skus,
        path_prefix=args.path_prefix,
        collection_path_slug=args.collection_path,
        limit=args.limit,
        apply=args.apply,
        repair_links=not args.no_repair_links,
        wait=args.wait,
        run_worker=args.run_worker,
        wait_timeout=args.wait_timeout,
    )
    print(json.dumps(result, indent=2, default=str))
    return 0 if result.get("status") in {"ok", "done", "queued", "skipped"} else 1


if __name__ == "__main__":
    raise SystemExit(main())
