"""Real, vendor-scoped per-SKU stock for Warehouse/Industry-sourced products
(see market/vendors.py) — deliberately NOT applied to Market-tied or generic
products, which keep using the existing flat, display-only
pickker_products.stock_qty column exactly as before (never decremented,
never blocks checkout — unchanged). A product only gets real stock tracking
once at least one row for it exists in pickker_vendor_stock; until then
get_available_qty treats it as unlimited, same as today.

pickker_vendor_stock.quantity is a maintained running total; every change to
it is also logged as an immutable row in pickker_stock_movements — the same
ledger-plus-current-total shape as market/earnings.py's picker payouts."""

from accounts.supabase_client import get_client


def get_vendor_stock(vendor_type, vendor_id, product_id):
    resp = (
        get_client().table('pickker_vendor_stock')
        .select('*')
        .eq('vendor_type', vendor_type).eq('vendor_id', vendor_id).eq('product_id', product_id)
        .execute()
    )
    return resp.data[0] if resp.data else None


def get_available_qty(vendor_type, vendor_id, product_id):
    """None means "not stock-tracked, unlimited" (the common case for every
    product outside this vendor scope) — callers should only treat an
    integer return value as an enforceable cap."""
    row = get_vendor_stock(vendor_type, vendor_id, product_id)
    return row['quantity'] if row else None


def get_stock_for_vendor(vendor_type, vendor_id):
    """Every stock-tracked product for this vendor, product name attached —
    used by the Stock tab (admin vendor page / company fleet dashboard)."""
    client = get_client()
    rows = (
        client.table('pickker_vendor_stock')
        .select('*')
        .eq('vendor_type', vendor_type).eq('vendor_id', vendor_id)
        .order('quantity')
        .execute().data
    )
    if not rows:
        return []
    product_ids = [r['product_id'] for r in rows]
    products_by_id = {p['id']: p for p in client.table('pickker_products').select('id, name, name_sw, unit').in_('id', product_ids).execute().data}
    for row in rows:
        product = products_by_id.get(row['product_id'], {})
        row['product_name'] = product.get('name', '—')
        row['unit'] = product.get('unit', '')
    return rows


_LOW_STOCK_THRESHOLD = 10


def get_low_stock_items(vendor_type, vendor_id):
    return [r for r in get_stock_for_vendor(vendor_type, vendor_id) if r['quantity'] < _LOW_STOCK_THRESHOLD]


def _record_movement(vendor_type, vendor_id, product_id, delta, reason, related_order_id=None, created_by=None):
    get_client().table('pickker_stock_movements').insert({
        'vendor_type': vendor_type, 'vendor_id': vendor_id, 'product_id': product_id,
        'delta': delta, 'reason': reason, 'related_order_id': related_order_id, 'created_by': created_by,
    }).execute()


def receive_stock(vendor_type, vendor_id, product_id, quantity, admin_user_id):
    """Admin/company logs newly-arrived stock — creates the tracked row on
    first receipt (a product with no pickker_vendor_stock row is untracked/
    unlimited right up until this is called for it), or tops up an existing
    one. quantity must be positive; use adjust_stock for a manual correction
    that can go either direction."""
    if quantity <= 0:
        raise ValueError('Received quantity must be positive')
    client = get_client()
    existing = get_vendor_stock(vendor_type, vendor_id, product_id)
    if existing:
        client.table('pickker_vendor_stock').update({
            'quantity': existing['quantity'] + quantity,
        }).eq('id', existing['id']).execute()
    else:
        client.table('pickker_vendor_stock').insert({
            'vendor_type': vendor_type, 'vendor_id': vendor_id, 'product_id': product_id, 'quantity': quantity,
        }).execute()
    _record_movement(vendor_type, vendor_id, product_id, quantity, 'received', created_by=admin_user_id)


def restore_stock(vendor_type, vendor_id, product_id, quantity, order_id=None):
    """Undoes a successful try_decrement_stock — used when a multi-item
    order's stock check fails partway through (one line item's decrement
    already succeeded before a later one is rejected as out of stock); the
    whole order is cancelled, so any stock already taken must go back
    rather than silently vanishing with no order to show for it."""
    row = get_vendor_stock(vendor_type, vendor_id, product_id)
    if not row:
        return
    get_client().table('pickker_vendor_stock').update({'quantity': row['quantity'] + quantity}).eq('id', row['id']).execute()
    _record_movement(vendor_type, vendor_id, product_id, quantity, 'adjustment', related_order_id=order_id)


def try_decrement_stock(vendor_type, vendor_id, product_id, quantity, order_id=None):
    """Atomic, concurrency-safe decrement via the pickker_try_decrement_stock
    Postgres function (a single conditional UPDATE, not a Python read-then-
    write, which would race two concurrent checkouts into overselling).
    Returns True and logs the movement on success; False (no state change)
    if the item isn't stock-tracked for this vendor, or has insufficient
    stock — caller treats False as "reject this checkout, out of stock"."""
    row = get_vendor_stock(vendor_type, vendor_id, product_id)
    if not row:
        return True  # not stock-tracked — unlimited, same as today's default
    resp = get_client().rpc('pickker_try_decrement_stock', {
        'p_vendor_type': vendor_type, 'p_vendor_id': vendor_id, 'p_product_id': product_id, 'p_qty': quantity,
    }).execute()
    if resp.data is None:
        return False
    _record_movement(vendor_type, vendor_id, product_id, -quantity, 'order_deducted', related_order_id=order_id)
    return True


def decrement_vendor_stock_for_order(order_id, items, vendor_type, vendor_id):
    """Records the vendor tie, then atomically decrements real stock for
    every item — on the first out-of-stock item, rolls back everything
    already decremented and cancels the order. Returns None on success, or
    the failed item's product dict. Shared by
    market/views.py::_make_vendor_fleet_after_create (web checkout, wraps
    the result in messages.error) and
    market/chat_checkout.py::schedule_order_from_chat (chat, turns it into
    an error dict) — extracted here so this logic can't drift between the
    two callers, which have no Django request/messages framework in
    common."""
    from .vendors import record_order_vendor_tie

    record_order_vendor_tie(order_id, vendor_type, vendor_id)
    decremented = []
    for item in items:
        if not try_decrement_stock(vendor_type, vendor_id, item['product']['id'], item['quantity'], order_id=order_id):
            for done in decremented:
                restore_stock(vendor_type, vendor_id, done['product']['id'], done['quantity'], order_id=order_id)
            get_client().table('pickker_orders').update({'status': 'cancelled'}).eq('id', order_id).execute()
            return item['product']
        decremented.append(item)
    return None
