"""Recompute BPC stock: group blueprint copies at a shop's bpc stockrooms into variants.

Each copy (memberaudit asset, is_blueprint_copy) is resolved to (me, te, runs) via the
CharacterBlueprint mirror (join by item_id); copies of the same (type, me, te, runs) are
counted into a BpcVariant. Prices on existing variants are preserved.
"""

from collections import defaultdict

from ...models import BPC, BpcListing, BpcVariant, CharacterBlueprint
from ...sources import source_assets


def _copy_assets(shop):
    """All blueprint-copy assets across the shop's bpc stockrooms."""
    for source in shop.sources.filter(section=BPC):
        yield from (
            source_assets(source)
            .filter(is_blueprint_copy=True)
            .select_related("eve_type")
        )


def recompute_bpc_stock(shop) -> None:
    # type_id -> {(me, te, runs): count}; type_id maps to an EveType via the asset.
    counts = defaultdict(lambda: defaultdict(int))
    type_obj = {}
    for asset in _copy_assets(shop):
        bp = CharacterBlueprint.objects.filter(item_id=asset.item_id).first()
        if bp is None:
            continue  # unresolvable — no ME/TE/runs
        counts[asset.eve_type_id][(bp.me, bp.te, bp.runs)] += 1
        type_obj[asset.eve_type_id] = asset.eve_type

    # Reconcile every existing listing (plus any new type) against the fresh counts.
    listings = {bl.eve_type_id: bl for bl in shop.bpc_listings.all()}
    for type_id in counts:
        if type_id not in listings:
            listings[type_id] = BpcListing.objects.create(
                shop=shop, eve_type=type_obj[type_id]
            )

    for type_id, listing in listings.items():
        target = counts.get(type_id, {})  # {(me, te, runs): count}; empty = type gone
        for (me, te, runs), n in target.items():
            BpcVariant.objects.update_or_create(
                listing=listing, me=me, te=te, runs=runs, defaults={"stock": n}
            )
        for v in listing.variants.all():
            if (v.me, v.te, v.runs) not in target:
                v.delete()
        if not listing.variants.exists():
            listing.delete()
