"""Staged master catalog intent preview and confirm pipeline."""

from __future__ import annotations

from datetime import datetime, timezone
from typing import Any, Dict, List, Optional

from sqlalchemy import func, select
from sqlalchemy.orm import Session

from db.master_catalog import import_master_sku_dataframe, registry_intent_records_from_dataframe
from db.master_taxonomy_registry import (
    apply_registry_fks_to_product,
    backfill_master_taxonomy_from_products,
    summarize_import_registry_intent,
)
from db.master_taxonomy_hierarchy import build_hierarchy_from_master_catalog
from db.models import MasterCatalogIntentRun, MasterProduct

STATUS_PREVIEW = "preview"
STATUS_CONFIRMED = "confirmed"
STATUS_COMPLETED = "completed"
STATUS_FAILED = "failed"

WRITE_MODE_PREVIEW_ONLY = "preview_only"
WRITE_MODE_STAGED_IMPORT = "staged_import"


def create_intent_preview_only_from_dataframe(
    session: Session,
    df,
    *,
    source_label: Optional[str] = None,
) -> Dict[str, Any]:
    """Analyze a master SKU CSV without writing products — intent run + preview DTO only."""
    import_rows, attribute_codes = registry_intent_records_from_dataframe(df)
    registry_intent = summarize_import_registry_intent(session, import_rows)
    taxonomy_intent = _taxonomy_intent_for_rows(session, import_rows)
    preview = build_intent_preview(
        session,
        source_label=source_label,
        registry_intent=registry_intent,
        taxonomy_intent=taxonomy_intent,
        attribute_codes=attribute_codes,
        sku_count=len(import_rows),
        write_mode=WRITE_MODE_PREVIEW_ONLY,
    )
    run = MasterCatalogIntentRun(
        status=STATUS_PREVIEW,
        source_label=source_label,
        preview_json=preview,
        sku_count=len(import_rows),
    )
    session.add(run)
    session.flush()
    return {
        "intent_run_id": run.id,
        "write_mode": WRITE_MODE_PREVIEW_ONLY,
        "preview": preview,
        "input_rows": len(df),
        "valid_skus": len(import_rows),
    }


def create_intent_staged_import_from_dataframe(
    session: Session,
    df,
    *,
    source_label: Optional[str] = None,
) -> Dict[str, Any]:
    """Persist master SKUs and create a staged intent run — no downstream provision."""
    stats = import_master_sku_dataframe(
        session,
        df,
        source_label=source_label or "intent_preview",
        resolve_registries=True,
    )
    preview = build_intent_preview(
        session,
        source_label=source_label,
        registry_intent=stats.get("registry_intent"),
        taxonomy_intent=stats.get("taxonomy_intent"),
        attribute_codes=stats.get("attribute_codes"),
        sku_count=int(stats.get("valid_skus") or 0),
        write_mode=WRITE_MODE_STAGED_IMPORT,
    )
    run = MasterCatalogIntentRun(
        status=STATUS_PREVIEW,
        source_label=source_label,
        preview_json=preview,
        sku_count=int(stats.get("valid_skus") or stats.get("inserted") or 0),
    )
    session.add(run)
    session.flush()
    return {
        "intent_run_id": run.id,
        "write_mode": WRITE_MODE_STAGED_IMPORT,
        "import": stats,
        "preview": preview,
    }


# Backward-compatible alias
create_intent_preview_from_dataframe = create_intent_staged_import_from_dataframe


def build_intent_preview(
    session: Session,
    *,
    source_label: Optional[str] = None,
    registry_intent: Optional[Dict[str, Any]] = None,
    taxonomy_intent: Optional[Dict[str, Any]] = None,
    attribute_codes: Optional[List[str]] = None,
    sku_count: Optional[int] = None,
    write_mode: Optional[str] = None,
) -> Dict[str, Any]:
    if registry_intent is None:
        products = list(session.scalars(select(MasterProduct).where(MasterProduct.is_active.is_(True)).limit(5000)).all())
        records = [
            {
                "sku": product.sku,
                "brand": product.brand,
                "product_family": product.product_family,
                "category_l1": product.category_l1,
                "collection": product.collection,
            }
            for product in products
        ]
        registry_intent = summarize_import_registry_intent(session, records)
        if taxonomy_intent is None:
            taxonomy_intent = _taxonomy_intent_for_rows(session, records)
        if sku_count is None:
            sku_count = len(products)
    elif taxonomy_intent is None and registry_intent.get("row_count"):
        taxonomy_intent = {"row_count": 0, "proposed_nodes": [], "ambiguous": [], "sku_samples": []}
    elif sku_count is None:
        sku_count = int(registry_intent.get("row_count") or 0)

    codes = set(attribute_codes or [])
    if not codes and write_mode != WRITE_MODE_PREVIEW_ONLY:
        from db.models import MasterProductAttributeValue

        for row in session.scalars(
            select(MasterProductAttributeValue.attribute_code).distinct().limit(500)
        ).all():
            if row:
                codes.add(str(row))

    return {
        "source_label": source_label,
        "write_mode": write_mode,
        "sku_count": sku_count,
        "registry_intent": registry_intent,
        "proposed_brands": (registry_intent.get("proposed_brands") or [])[:50],
        "proposed_collections": (registry_intent.get("proposed_collections") or [])[:50],
        "ambiguous": (registry_intent.get("ambiguous") or [])[:50],
        "taxonomy_intent": {
            "matched_counts": taxonomy_intent.get("matched_counts") if taxonomy_intent else {},
            "proposed_nodes": (taxonomy_intent.get("proposed_nodes") or [])[:50] if taxonomy_intent else [],
            "ambiguous": (taxonomy_intent.get("ambiguous") or [])[:50] if taxonomy_intent else [],
            "sku_samples": (taxonomy_intent.get("sku_samples") or [])[:25] if taxonomy_intent else [],
        },
        "attribute_codes": sorted(codes),
        "product_type_inferences": (registry_intent.get("product_type_inferences") or [])[:50],
    }


def get_intent_run(session: Session, run_id: int) -> Dict[str, Any]:
    run = session.get(MasterCatalogIntentRun, int(run_id))
    if run is None:
        raise ValueError("Intent run not found")
    return _run_dict(run)


def confirm_intent_run(
    session: Session,
    run_id: int,
    *,
    create_registry_rows: bool = False,
    link_products: bool = True,
    run_backfill: bool = False,
    build_hierarchy: bool = False,
    approved_taxonomy_proposals: Optional[List[Dict[str, Any]]] = None,
    sync_taxonomy_channels: bool = False,
    magento_connection_id: Optional[int] = None,
    shopify_connection_id: Optional[int] = None,
    dry_run: bool = False,
    allow_taxonomy_invent: Optional[bool] = None,
) -> Dict[str, Any]:
    from db.taxonomy_invent_policy import can_auto_invent, taxonomy_auto_invent_enabled

    run = session.get(MasterCatalogIntentRun, int(run_id))
    if run is None:
        raise ValueError("Intent run not found")
    if run.status not in {STATUS_PREVIEW, STATUS_CONFIRMED}:
        raise ValueError(f"Intent run status not confirmable: {run.status}")

    write_mode = (run.preview_json or {}).get("write_mode")
    product_count = session.scalar(
        select(func.count()).select_from(MasterProduct).where(MasterProduct.is_active.is_(True))
    )
    if write_mode == WRITE_MODE_PREVIEW_ONLY and int(product_count or 0) == 0:
        raise ValueError(
            "Intent run was preview_only with no imported SKUs; import master SKUs before confirm"
        )

    invent_ok = can_auto_invent(allow_invent=allow_taxonomy_invent)
    # When invent is locked, force invent-capable steps off regardless of caller defaults.
    if not invent_ok:
        create_registry_rows = False
        run_backfill = False
        build_hierarchy = False

    result: Dict[str, Any] = {
        "dry_run": dry_run,
        "write_mode": write_mode,
        "invent_allowed": invent_ok,
    }
    if dry_run:
        result["would_backfill"] = backfill_master_taxonomy_from_products(
            session, dry_run=True, allow_invent=invent_ok
        )
        preview = run.preview_json or {}
        proposals = approved_taxonomy_proposals or (preview.get("taxonomy_intent") or {}).get("proposed_nodes") or []
        if proposals:
            from db.master_taxonomy_intent import apply_taxonomy_proposals

            result["would_apply_taxonomy"] = apply_taxonomy_proposals(
                session, proposals, dry_run=True, allow_invent=invent_ok
            )
        run.status = STATUS_PREVIEW
        session.flush()
        return result

    with taxonomy_auto_invent_enabled(invent_ok):
        run.status = STATUS_CONFIRMED
        run.confirmed_at = datetime.now(timezone.utc)

        if run_backfill or create_registry_rows:
            result["backfill"] = backfill_master_taxonomy_from_products(
                session, dry_run=False, allow_invent=invent_ok
            )

        preview = run.preview_json or {}
        proposals = approved_taxonomy_proposals
        if proposals is None:
            proposals = (preview.get("taxonomy_intent") or {}).get("proposed_nodes") or []
        if proposals:
            from db.master_taxonomy_intent import apply_taxonomy_proposals

            result["taxonomy_nodes"] = apply_taxonomy_proposals(
                session,
                proposals,
                dry_run=False,
                sync_channels=sync_taxonomy_channels,
                magento_connection_id=magento_connection_id,
                shopify_connection_id=shopify_connection_id,
                allow_invent=invent_ok,
            )

        if build_hierarchy:
            result["hierarchy"] = build_hierarchy_from_master_catalog(
                session, dry_run=False, allow_invent=invent_ok
            )

        if link_products:
            linked = 0
            for product in session.scalars(select(MasterProduct).where(MasterProduct.is_active.is_(True))).all():
                applied = apply_registry_fks_to_product(
                    session, product, create_missing=create_registry_rows, allow_invent=invent_ok
                )
                if applied:
                    linked += 1
            result["products_linked"] = linked

        run.status = STATUS_COMPLETED
        run.completed_at = datetime.now(timezone.utc)
        run.result_json = result
        session.flush()
        return {"intent_run_id": run.id, **result}


def _taxonomy_intent_for_rows(session: Session, rows: List[Dict[str, Any]]) -> Dict[str, Any]:
    from db.master_taxonomy_intent import summarize_import_taxonomy_intent

    return summarize_import_taxonomy_intent(session, rows)


def _run_dict(run: MasterCatalogIntentRun) -> Dict[str, Any]:
    return {
        "id": run.id,
        "status": run.status,
        "source_label": run.source_label,
        "sku_count": run.sku_count,
        "preview": run.preview_json,
        "result": run.result_json,
        "confirmed_at": run.confirmed_at.isoformat() if run.confirmed_at else None,
        "completed_at": run.completed_at.isoformat() if run.completed_at else None,
        "created_at": run.created_at.isoformat() if run.created_at else None,
    }
