"""Feed Manager for Readsb feed."""
from aiohttp import ClientSession

from .consts import T_FEED_ENTRY, UPDATE_OK, UPDATE_OK_NO_DATA
from typing import Awaitable, Callable, List, Optional, Set
import datetime
import logging
from .feed import ReadsbFeed
from .feed_entry import ReadsbFeedEntry

_LOGGER = logging.getLogger(__name__)

class ReadsbFeedManager():
    """Feed Manager for Readsb feed."""

    def __init__(self,
                 websession: ClientSession,
                 generate_callback,
                 update_callback,
                 remove_callback,
                 coordinates=None,
                 feed_url=None,
                 filter_radius=None):
        """Initialize the Readsb Manager."""
        feed = ReadsbFeed(
            websession,
            coordinates,
            feed_url,
            filter_radius)


    async def _update_internal(
        self, status: str, feed_entries: Optional[List[T_FEED_ENTRY]]
    ):
        """Update the feed and then update connected entities."""
        # Record current time of update.
        self._last_update = datetime.now()
        await self._store_feed_entries(status, feed_entries)
        if status == UPDATE_OK:
            _LOGGER.debug("Data retrieved %s", feed_entries)
            # Record current time of update.
            self._last_update_successful = self._last_update
            # For entity management the external ids from the feed are used.
            feed_external_ids = set([entry.external_id for entry in feed_entries])
            await self._update_feed_remove_entries(feed_external_ids)
            await self._update_feed_update_entries(feed_external_ids)
            await self._update_feed_create_entries(feed_external_ids)
        elif status == UPDATE_OK_NO_DATA:
            _LOGGER.debug("Update successful, but no data received from %s", self._feed)
            # Record current time of update.
            self._last_update_successful = self._last_update
        else:
            _LOGGER.warning(
                "Update not successful, no data received from %s", self._feed
            )

    async def update(self):
        """Update the feed and then update connected entities."""
        status, feed_entries = await self._feed.update()
        await self._update_internal(status, feed_entries)

    async def update_override(self):
        """Update the feed and then update connected entities."""
        status, feed_entries = await self._feed.update_override()
        await self._update_internal(status, feed_entries)

    async def _store_feed_entries(
        self, status: str, feed_entries: Optional[List[ReadsbFeedEntry]]
    ):
        """Keep a copy of all feed entries for future lookups."""
        if feed_entries or status == UPDATE_OK_NO_DATA:
            if status == UPDATE_OK:
                self.feed_entries = {entry.external_id: entry for entry in feed_entries}
        else:
            self.feed_entries.clear()

    async def _update_feed_create_entries(self, feed_external_ids: Set[str]) -> int:
        """Create entities after feed update."""
        create_external_ids = feed_external_ids.difference(self._managed_external_ids)
        count_created = len(create_external_ids)
        await self._generate_new_entities(create_external_ids)
        return count_created

    async def _update_feed_update_entries(self, feed_external_ids: Set[str]) -> int:
        """Update entities after feed update."""
        update_external_ids = self._managed_external_ids.intersection(feed_external_ids)
        count_updated = len(update_external_ids)
        await self._update_entities(update_external_ids)
        return count_updated

    async def _update_feed_remove_entries(self, feed_external_ids: Set[str]) -> int:
        """Remove entities after feed update."""
        remove_external_ids = self._managed_external_ids.difference(feed_external_ids)
        count_removed = len(remove_external_ids)
        await self._remove_entities(remove_external_ids)
        return count_removed

    async def _generate_new_entities(self, external_ids: Set[str]):
        """Generate new entities for events using callback."""
        for external_id in external_ids:
            await self._generate_async_callback(external_id)
            _LOGGER.debug("New entity added %s", external_id)
            self._managed_external_ids.add(external_id)

    async def _update_entities(self, external_ids: Set[str]):
        """Update entities using callback."""
        for external_id in external_ids:
            _LOGGER.debug("Existing entity found %s", external_id)
            await self._update_async_callback(external_id)

    async def _remove_entities(self, external_ids: Set[str]):
        """Remove entities using callback."""
        for external_id in external_ids:
            _LOGGER.debug("Entity not current anymore %s", external_id)
            self._managed_external_ids.remove(external_id)
            await self._remove_async_callback(external_id)
