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

Each copy (corptools asset, blueprint_copy=True) carries its own ME/TE/runs directly on
the corptools asset; 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
from ...sources import source_assets

logger = logging.getLogger(__name__)


def _copy_assets(shop):
    """Yield blueprint-copy assets (carrying ME/TE/runs) across the shop's bpc
    stockrooms."""
    for source in shop.sources.filter(section=BPC):
        yield from source_assets(source).filter(
            blueprint_copy=True, material_efficiency__isnull=False
        )


def recompute_bpc_stock(shop) -> None:
    counts = defaultdict(lambda: defaultdict(int))
    type_obj = {}
    for asset in _copy_assets(shop):
        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][
            (asset.material_efficiency, asset.time_efficiency, asset.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()
