"""ESI blueprints provider + local mirror.

memberaudit doesn't store ME/TE/runs, so we pull /characters/{id}/blueprints/ with the
owner's read_blueprints token (from AA's shared esi.Token store) and mirror it locally,
keyed by item_id (== memberaudit CharacterAsset.item_id, the stock-join key).
"""

from functools import lru_cache

from ...models import CharacterBlueprint

BLUEPRINTS_SCOPE = "esi-characters.read_blueprints.v1"
COMPATIBILITY_DATE = "2025-12-16"


@lru_cache(maxsize=1)
def _esi():
    from esi.openapi_clients import ESIClientProvider

    from ... import __version__

    return ESIClientProvider(
        compatibility_date=COMPATIBILITY_DATE,
        ua_appname="aa-shop",
        ua_version=__version__,
        operations=["GetCharactersCharacterIdBlueprints"],
    )


def _field(row, name):
    return row[name] if isinstance(row, dict) else getattr(row, name)


def blueprint_token(character):
    """A valid esi.Token for this character carrying the blueprints scope, or None."""
    from esi.models import Token

    eve_char_id = character.eve_character.character_id
    return (
        Token.objects.filter(character_id=eve_char_id)
        .require_scopes([BLUEPRINTS_SCOPE])
        .require_valid()
        .first()
    )


def fetch_blueprints(character) -> list:
    """Fetch this character's blueprints from ESI. Returns [] if no usable token."""
    token = blueprint_token(character)
    if token is None:
        return []
    eve_char_id = character.eve_character.character_id
    return (
        _esi()
        .client.Character.GetCharactersCharacterIdBlueprints(
            character_id=eve_char_id, token=token
        )
        .results(use_etag=False)
    )


def store_blueprints(character, rows) -> int:
    """Upsert the mirror for one character from ESI-shaped rows; prune stale.

    Returns the number of rows stored.
    """
    seen = []
    for r in rows:
        item_id = _field(r, "item_id")
        CharacterBlueprint.objects.update_or_create(
            item_id=item_id,
            defaults={
                "character": character,
                "type_id": _field(r, "type_id"),
                "me": _field(r, "material_efficiency"),
                "te": _field(r, "time_efficiency"),
                "runs": _field(r, "runs"),
                "quantity": _field(r, "quantity"),
            },
        )
        seen.append(item_id)
    CharacterBlueprint.objects.filter(character=character).exclude(
        item_id__in=seen
    ).delete()
    return len(seen)


def refresh_character_blueprints(character) -> int:
    """Fetch from ESI + mirror. Returns rows stored (0 if no token)."""
    return store_blueprints(character, fetch_blueprints(character))
