from typing import Any, List, Set, Tuple, Type

from google.appengine.ext import ndb

from backend.common.models.cached_model import TAffectedReferences
from backend.common.models.district_team import DistrictTeam
from backend.common.models.event import Event
from backend.common.models.event_team import EventTeam
from backend.common.queries import (
    award_query,
    district_query,
    event_details_query,
    event_query,
    match_query,
    media_query,
    robot_query,
    team_query,
)
from backend.common.queries.database_query import CachedDatabaseQuery

TCacheKeyAndQuery = Tuple[str, Type[CachedDatabaseQuery]]


def _queries_to_cache_keys_and_queries(
    queries: List[CachedDatabaseQuery],
) -> List[TCacheKeyAndQuery]:
    out = []
    for query in queries:
        out.append((query.cache_key, type(query)))
    return out


def _filter(refs: Set[Any]) -> Set[Any]:
    # Default filter() filters zeros, so we can't use it.
    return {r for r in refs if r is not None}


def award_updated(affected_refs: TAffectedReferences) -> List[TCacheKeyAndQuery]:
    event_keys = _filter(affected_refs["event"])
    team_keys = _filter(affected_refs["team_list"])
    years = _filter(affected_refs["year"])
    event_types = _filter(affected_refs["event_type_enum"])
    award_types = _filter(affected_refs["award_type_enum"])

    queries: List[CachedDatabaseQuery] = []
    for event_key in event_keys:
        queries.append(award_query.EventAwardsQuery(event_key.id()))
        for team_key in team_keys:
            queries.append(
                award_query.TeamEventAwardsQuery(team_key.id(), event_key.id())
            )

    for team_key in team_keys:
        queries.append(award_query.TeamAwardsQuery(team_key.id()))
        for year in years:
            queries.append(award_query.TeamYearAwardsQuery(team_key.id(), year))
        for event_type in event_types:
            for award_type in award_types:
                queries.append(
                    award_query.TeamEventTypeAwardsQuery(
                        team_key.id(), event_type, award_type
                    )
                )

    return _queries_to_cache_keys_and_queries(queries)


def event_updated(affected_refs: TAffectedReferences) -> List[TCacheKeyAndQuery]:
    event_keys = _filter(affected_refs["key"])
    years = _filter(affected_refs["year"])
    event_district_keys = _filter(affected_refs["district_key"])

    event_team_keys_future = EventTeam.query(
        EventTeam.event.IN([event_key for event_key in event_keys])  # pyre-ignore[16]
    ).fetch_async(None, keys_only=True)
    events_future = ndb.get_multi_async(event_keys)

    queries: List[CachedDatabaseQuery] = []
    for event_key in event_keys:
        queries.append(event_query.EventQuery(event_key.id()))
        queries.append(event_query.EventDivisionsQuery(event_key.id()))

    for year in years:
        queries.append(event_query.EventListQuery(year))

    for event_district_key in event_district_keys:
        queries.append(event_query.DistrictEventsQuery(event_district_key.id()))

    if event_keys:
        for et_key in event_team_keys_future.get_result():
            team_key = et_key.id().split("_")[1]
            year = int(et_key.id()[:4])
            queries.append(event_query.TeamEventsQuery(team_key))
            queries.append(event_query.TeamYearEventsQuery(team_key, year))
            queries.append(event_query.TeamYearEventTeamsQuery(team_key, year))

    events_with_parents = filter(
        lambda e: e.get_result() is not None
        and e.get_result().parent_event is not None,
        events_future,
    )
    parent_keys = set([e.get_result().parent_event for e in events_with_parents])
    for parent_key in parent_keys:
        queries.append(event_query.EventDivisionsQuery(parent_key.id()))

    return _queries_to_cache_keys_and_queries(queries)


def event_details_updated(
    affected_refs: TAffectedReferences,
) -> List[TCacheKeyAndQuery]:
    event_details_keys = _filter(affected_refs["key"])

    queries: List[CachedDatabaseQuery] = []
    for event_details_key in event_details_keys:
        queries.append(event_details_query.EventDetailsQuery(event_details_key.id()))

    return _queries_to_cache_keys_and_queries(queries)


def match_updated(affected_refs: TAffectedReferences) -> List[TCacheKeyAndQuery]:
    match_keys = _filter(affected_refs["key"])
    event_keys = _filter(affected_refs["event"])
    team_keys = _filter(affected_refs["team_keys"])