"""Shop asset sourcing over corptools: query the assets at a single ShopSource
and enumerate a user's characters/corporations/locations for the stockroom picker."""

from corptools.models import (
    CharacterAsset,
    CharacterAudit,
    CorpAsset,
    CorporationAudit,
    EveLocation,
)
from django.db.models import Sum

HANGAR_FLAG = "Hangar"


def _is_corp_source(source) -> bool:
    return source.corporation_audit_id is not None


def source_assets(source):
    """Assets located at a single ShopSource (corptools, flat containment)."""
    if _is_corp_source(source):
        qs = CorpAsset.objects.filter(corporation_id=source.corporation_audit_id)
        if source.container_item_id:
            return qs.filter(location_id=source.container_item_id)
        return qs.filter(location_name_id=source.location_id)
    qs = CharacterAsset.objects.filter(character_id=source.character_audit_id)
    if source.container_item_id:
        return qs.filter(location_id=source.container_item_id)
    return qs.filter(location_id=source.location_id, location_flag=HANGAR_FLAG)


def source_qty(source, eve_type) -> int:
    """Quantity of `eve_type` (matched by type_id) available at a single source."""
    n = (
        source_assets(source)
        .filter(type_id=eve_type.id)
        .aggregate(n=Sum("quantity"))["n"]
    )
    return n or 0


def owner_characters(user):
    """corptools CharacterAudits owned by `user`."""
    return CharacterAudit.objects.filter(
        character__character_ownership__user=user
    ).select_related("character")


def owner_corporations(user):
    """corptools CorporationAudits the user may source from (own/alliance/director/
    global, per corptools' own visibility rules)."""
    return CorporationAudit.objects.visible_to(user).select_related("corporation")


def _location_name(location_id) -> str:
    loc = EveLocation.objects.filter(location_id=location_id).first()
    return loc.location_name if loc else str(location_id)


def container_name_for(owner_id, item_id, *, is_corp=False) -> str:
    if not item_id:
        return ""
    if is_corp:
        model, field = CorpAsset, "corporation_id"
    else:
        model, field = CharacterAsset, "character_id"
    asset = (
        model.objects.filter(**{field: owner_id}, item_id=item_id)
        .select_related("type_name")
        .first()
    )
    if asset is None:
        return ""
    return asset.name or (
        asset.type_name.name if asset.type_name else str(asset.type_id)
    )


def _is_ship(asset) -> bool:
    try:
        tn = asset.type_name
        return bool(tn and tn.group_id and tn.group.category_id == 6)
    except Exception:
        return False


def source_tree(user, shop):
    """Nested owner -> locations -> containers tree for the source picker.
    Dispatches on shop kind; both kinds share the same JSON shape."""
    from .models import Shop

    if shop.shop_type == Shop.ShopType.CORPORATION:
        return _corp_source_tree(user)
    return _character_source_tree(user)


def _character_source_tree(user):
    tree = []
    for audit in owner_characters(user):
        assets = list(
            CharacterAsset.objects.filter(character=audit).select_related(
                "type_name__group"
            )
        )
        parent_ids = {a.location_id for a in assets}
        stations = {}
        for a in assets:
            if a.location_flag != HANGAR_FLAG:
                continue
            st = stations.setdefault(
                a.location_id,
                {
                    "location_id": a.location_id,
                    "name": _location_name(a.location_id),
                    "containers": [],
                },
            )
            if a.item_id in parent_ids and not _is_ship(a):
                st["containers"].append(
                    {
                        "item_id": a.item_id,
                        "name": a.name
                        or (a.type_name.name if a.type_name else str(a.type_id)),
                    }
                )
        for st in stations.values():
            st["containers"].sort(key=lambda c: c["name"].lower())
        if stations:
            tree.append(
                {
                    "owner_id": audit.pk,
                    "owner_name": audit.character.character_name,
                    "locations": sorted(
                        stations.values(), key=lambda s: s["name"].lower()
                    ),
                }
            )
    return tree


def _corp_source_tree(user):
    """Corporation -> divisions -> containers tree. Divisions are corptools' managed
    per-division EveLocations; a division's containers are container assets held
    directly in it (ships excluded)."""
    tree = []
    for corp_audit in owner_corporations(user):
        divisions = {
            loc.location_id: {
                "location_id": loc.location_id,
                "name": loc.location_name,
                "containers": [],
            }
            for loc in EveLocation.objects.filter(
                managed=True, managed_corp=corp_audit, location_id__lt=0
            )
        }
        if not divisions:
            continue
        # A container is any asset whose item_id is some asset's location_id. Its
        # contents are NOT division-tagged, so derive parent_ids from ALL the corp's
        # assets (mirrors the character path, which scans the whole character).
        parent_ids = set(
            CorpAsset.objects.filter(corporation=corp_audit)
            .values_list("location_id", flat=True)
            .distinct()
        )
        assets = list(
            CorpAsset.objects.filter(
                corporation=corp_audit, location_name_id__in=list(divisions)
            ).select_related("type_name__group")
        )
        for a in assets:
            div = divisions.get(a.location_name_id)
            if div is None:
                continue
            if a.item_id in parent_ids and not _is_ship(a):
                div["containers"].append(
                    {
                        "item_id": a.item_id,
                        "name": a.name
                        or (a.type_name.name if a.type_name else str(a.type_id)),
                    }
                )
        for div in divisions.values():
            div["containers"].sort(key=lambda c: c["name"].lower())
        tree.append(
            {
                "owner_id": corp_audit.pk,
                "owner_name": corp_audit.corporation.corporation_name,
                "locations": sorted(
                    divisions.values(), key=lambda s: s["name"].lower()
                ),
            }
        )
    return tree
