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

Each copy (corptools asset, blueprint_copy=True) 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.
"""

import logging
from collections import defaultdict

from eveuniverse.models import EveType

from ...models import (
    BPC,
    BpcListing,
    BpcVariant,
    CharacterBlueprint,
    CorporationBlueprint,
)
from ...sources import source_assets

logger = logging.getLogger(__name__)


def _copy_assets(shop):
    """Yield (asset, is_corp) for each blueprint-copy asset across the shop's bpc
    stockrooms; `is_corp` selects which ME/TE/runs mirror to join."""
    for source in shop.sources.filter(section=BPC):
        is_corp = source.corporation_audit_id is not None
        for asset in source_assets(source).filter(blueprint_copy=True):
            yield asset, is_corp


def recompute_bpc_stock(shop) -> None:
    counts = defaultdict(lambda: defaultdict(int))
    type_obj = {}
    for asset, is_corp in _copy_assets(shop):
        mirror = CorporationBlueprint if is_corp else CharacterBlueprint
        bp = mirror.objects.filter(item_id=asset.item_id).first()
        if bp is None:
            continue  # unresolvable — no ME/TE/runs
        if asset.type_id not in type_obj:
            try:
                type_obj[asset.type_id], _ = EveType.objects.get_or_create_esi(
                    id=asset.type_id
                )
            except Exception:
                logger.warning("skipping unresolvable bpc type %s", asset.type_id)
                continue
        counts[asset.type_id][(bp.me, bp.te, bp.runs)] += 1

    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, {})
        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()
