"""
Push near-real-time stock-quantity updates to the CRM.

The ERP is the source of truth for physical inventory and reservations. The CRM
caches a single `available_stock_quantity` per product (keyed by `erp_product_id`)
that it uses to gate quoting / selling. Whenever ERP availability changes — a
stock reservation is created/approved/expired/released, or a CRM-pushed order
reserves stock — we broadcast the new totals to the CRM so both systems agree.

This is the ERP -> CRM half of the two-way stock sync (the CRM -> ERP half is the
order push, which reserves stock in the ERP). Fire-and-forget: runs in a daemon
thread with a short timeout and never raises into the request path. No-op unless
a CRM stock webhook URL is configured.
"""
from __future__ import annotations

import logging
import threading

import requests

logger = logging.getLogger(__name__)


def _resolve_stock_webhook_config():
    from system_administration.host_policy import resolve_crm_webhook

    return resolve_crm_webhook("crm_stock_webhook_url")


def _post(url, secret, products):
    try:
        requests.post(
            url,
            json={"products": products},
            headers={"X-Webhook-Secret": secret or ""},
            timeout=8,
        )
    except Exception as exc:  # noqa: BLE001 — never break the caller
        logger.warning("CRM stock webhook failed: %s", exc)


def _build_product_payload(product_ids):
    """Compute current/reserved/available totals (summed across every branch
    warehouse) for each affected product, returning CRM-ready dicts."""
    from warehouse_management.models import Product
    from warehouse_management.stock_availability import get_inventory_totals

    payload = []
    seen = set()
    for pid in product_ids or []:
        if pid is None:
            continue
        try:
            pid_int = int(pid)
        except (TypeError, ValueError):
            continue
        if pid_int in seen:
            continue
        seen.add(pid_int)

        try:
            product = Product.objects.get(id=pid_int)
        except Product.DoesNotExist:
            continue

        current = reserved = available = 0.0
        warehouses = set()
        for inv in product.product_inventories.filter(recycle_bin=False).select_related("warehouse"):
            if inv.warehouse is None or inv.warehouse.id in warehouses:
                continue
            warehouses.add(inv.warehouse.id)
            totals = get_inventory_totals(product, inv.warehouse)
            current += totals["current_quantity"]
            reserved += totals["reserved_quantity"]
            available += totals["available_quantity"]

        payload.append({
            "erp_product_id": str(product.id),
            "stock_keeping_unit": product.stock_keeping_unit or "",
            "current_quantity": str(current),
            "reserved_quantity": str(reserved),
            "available_quantity": str(available),
        })
    return payload


def notify_crm_stock(product_ids):
    """Queue a CRM stock-quantity refresh for the given ERP product id(s)."""
    url, secret = _resolve_stock_webhook_config()
    if not url:
        return

    try:
        products = _build_product_payload(product_ids)
    except Exception as exc:  # noqa: BLE001 — never break the caller
        logger.warning("stock payload build failed: %s", exc)
        return
    if not products:
        return

    threading.Thread(target=_post, args=(url, secret, products), daemon=True).start()
