"""Component to manage a shopping list."""
import asyncio
import logging
import uuid
import json
from homeassistant.core import callback
from homeassistant.helpers import intent
from homeassistant.util.json import load_json, save_json
import homeassistant.ais_dom.ais_global as ais_global

DOMAIN = 'ais_bookmarks'
DEPENDENCIES = ['http']
_LOGGER = logging.getLogger(__name__)
INTENT_ADD_FAVORITE = 'AisBookmarksAddFavorite'
INTENT_ADD_BOOKMARK = 'AisBookmarksAddBookmark'
INTENT_LAST_BOOKMARKS = 'AisBookmarksLastBookmarks'
INTENT_PLAY_LAST_BOOKMARK = 'AisBookmarkPlayLastBookmark'
PERSISTENCE_BOOKMARKS = '.dom/.ais_bookmarks.json'
PERSISTENCE_FAVORITES = '.dom/.ais_favorites.json'

SERVICE_ADD_BOOKMARK = 'add_bookmark'
SERVICE_ADD_FAVORITE = 'add_favorite'
SERVICE_GET_BOOKMARKS = 'get_bookmarks'
SERVICE_GET_FAVORITES = 'get_favorites'
SERVICE_PLAY_BOOKMARK = 'play_bookmark'
SERVICE_PLAY_FAVORITE = 'play_favorite'
SERVICE_DELETE_BOOKMARK = 'delete_bookmark'
SERVICE_DELETE_FAVORITE = 'delete_favorite'


@asyncio.coroutine
def async_setup(hass, config):
    """Initialize the ais bookmarks list."""
    @asyncio.coroutine
    def add_bookmark_service(call):
        """Add an current played item to bookmark"""
        d = hass.data[DOMAIN]
        d.async_add(call, True)

    @asyncio.coroutine
    def add_favorite_service(call):
        """Add an current played item to favorite."""
        d = hass.data[DOMAIN]
        d.async_add(call, False)

    @asyncio.coroutine
    def get_bookmarks_service(call):
        """Return the list of bookmarks to app list"""
        d = hass.data[DOMAIN]
        list_info = {}
        list_idx = 0
        for item in reversed(d.bookmarks):
            if "media_stream_image" in item and item["media_stream_image"] is not None:
                img = item["media_stream_image"]
            else:
                img = "/static/icons/tile-win-310x150.png"
            list_info[list_idx] = {}
            list_info[list_idx]["title"] = item['name']
            if item['name'].startswith(item['source']):
                list_info[list_idx]["name"] = item['name']
            else:
                list_info[list_idx]["name"] =\
                    ais_global.G_NAME_FOR_AUDIO_NATURE.get(item['source'], item['source']) + " " + item['name']
            list_info[list_idx]["thumbnail"] = img
            list_info[list_idx]["uri"] = item["media_content_id"]
            list_info[list_idx]["audio_type"] = item['source']
            list_info[list_idx]["media_source"] = item['source']
            list_info[list_idx]["icon"] = 'mdi:bookmark-music'
            list_info[list_idx]["icon_remove"] = 'mdi:delete-forever'
            list_info[list_idx]["id"] = item['id']
            list_info[list_idx]["media_position"] = ais_global.get_milliseconds_formated(item['media_position'])
            list_info[list_idx]["media_position_ms"] = item['media_position']
            list_idx = list_idx + 1
        # create lists
        hass.states.async_set("sensor.aisbookmarkslist", -1, list_info)

    @asyncio.coroutine
    def get_favorites_service(call):
        """Return the list of favorites to app list"""
        audio_source = None
        if "audio_source" in call.data:
            audio_source = call.data["audio_source"]

        d = hass.data[DOMAIN]
        list_info = {}
        list_idx = 0
        for item in reversed(d.favorites):
            if audio_source is None or audio_source == item['source']:
                if "media_stream_image" in item and item["media_stream_image"] is not None:
                    img = item["media_stream_image"]
                else:
                    img = "/static/icons/tile-win-310x150.png"
                list_info[list_idx] = {}
                list_info[list_idx]["title"] = item['name']
                if item['name'].startswith(item['source']):
                    list_info[list_idx]["name"] = item['name']
                else:
                    list_info[list_idx]["name"] =\
                        ais_global.G_NAME_FOR_AUDIO_NATURE.get(item['source'], item['source']) + " " + item['name']
                list_info[list_idx]["thumbnail"] = img
                list_info[list_idx]["uri"] = item["media_content_id"]
                list_info[list_idx]["audio_type"] = item['source']
                list_info[list_idx]["icon_type"] = ais_global.G_ICON_FOR_AUDIO.get(item['source'], 'mdi:play')
                list_info[list_idx]["icon_remove"] = 'mdi:delete-forever'
                if audio_source == ais_global.G_AN_PODCAST:
                    list_info[list_idx]["icon"] = 'mdi:podcast'
                else:
                    list_info[list_idx]["icon"] = 'mdi:play'
                list_info[list_idx]["id"] = item['id']
                list_idx = list_idx + 1

        # create lists
        if audio_source is None:
            # get all items
            hass.states.async_set("sensor.aisfavoriteslist", -1, list_info)
        else:
            # check if the change was done form remote
            import homeassistant.components.ais_ai_service as ais_ai
            if audio_source == ais_global.G_AN_RADIO:
                hass.states.async_set("sensor.radiolist", -1, list_info)
                if ais_ai.CURR_ENTITIE == 'input_select.radio_type' and ais_ai.CURR_BUTTON_CODE == 23:
                    ais_ai.set_curr_entity(hass, 'sensor.radiolist')
                    hass.async_add_job(hass.services.async_call('ais_ai_service', 'say_it', {"text": "Wybierz stację"}))
            elif audio_source == ais_global.G_AN_PODCAST:
                hass.states.async_set("sensor.podcastnamelist", -1, list_info)
                if ais_ai.CURR_ENTITIE == 'input_select.podcast_type' and ais_ai.CURR_BUTTON_CODE == 23:
                    ais_ai.set_curr_entity(hass, 'sensor.podcastnamelist')
                    hass.async_add_job(hass.services.async_call('ais_ai_service', 'say_it', {"text": "Wybierz audycję"}))
            elif audio_source == ais_global.G_AN_MUSIC:
                hass.states.async_set("sensor.youtubelist", -1, list_info)
            elif audio_source == ais_global.G_AN_SPOTIFY:
                hass.states.async_set("sensor.spotifylist", -1, list_info)
            elif audio_source == ais_global.G_AN_AUDIOBOOK:
                hass.states.async_set("sensor.audiobookschapterslist", -1, list_info)


    @asyncio.coroutine
    def play_bookmark_service(call):
        """Play selected bookmark"""
        bookmark_id = int(call.data.get("id"))
        # get item from list
        state = hass.states.get("sensor.aisbookmarkslist")
        attr = state.attributes
        track = attr.get(bookmark_id)

        #
        if track['media_source'] == ais_global.G_AN_LOCAL:
            last_slash = track["uri"].rfind("/")
            if last_slash > 0:
                dir_path = track["uri"][0:last_slash]
            else:
                dir_path = track["uri"]
            hass.async_add_job(hass.services.async_call(
                'ais_drives_service', 'browse_path',
                {"path": dir_path, "file_path": track["uri"], "seek_position": track["media_position_ms"]}))

        elif track['media_source'] == ais_global.G_AN_MUSIC:
            hass.async_add_job(
                hass.services.async_call(
                    'ais_yt_service', 'select_track_uri', {"id": bookmark_id, "media_source": ais_global.G_AN_BOOKMARK})
            )
        else:
            _audio_info = json.dumps({"IMAGE_URL": track["thumbnail"], "NAME": track['title'],
                                      "audio_type": track['audio_type'],
                                      "MEDIA_SOURCE": track['media_source'],
                                      "media_content_id": track['uri'],
                                      "media_position_ms": track["media_position_ms"]})
            # set stream uri, image and title
            hass.async_add_job(
                hass.services.async_call(
                    'media_player',
                    'play_media', {
                        "entity_id": ais_global.G_LOCAL_EXO_PLAYER_ENTITY_ID,
                        "media_content_type": "ais_content_info",
                        "media_content_id": _audio_info
                    })
            )

    @asyncio.coroutine
    def play_favorite_service(call):
        """Play selected favorite"""
        favorite_id = int(call.data.get("id"))
        # get item from list
        state = hass.states.get("sensor.aisfavoriteslist")
        attr = state.attributes
        track = attr.get(favorite_id)

        _audio_info = json.dumps({"IMAGE_URL": track["thumbnail"], "NAME": track['title'],
                                  "audio_type": track['audio_type'],
                                  "MEDIA_SOURCE": ais_global.G_AN_FAVORITE, "media_content_id": track['uri']})
        # set stream uri, image and title
        hass.async_add_job(
            hass.services.async_call(
                'media_player',
                'play_media', {
                    "entity_id": ais_global.G_LOCAL_EXO_PLAYER_ENTITY_ID,
                    "media_content_type": "ais_content_info",
                    "media_content_id": _audio_info
                })
        )

    @asyncio.coroutine
    def delete_bookmark_service(call):
        """Delete selected bookmark"""
        bookmark_id = int(call.data.get("id"))
        state = hass.states.get('sensor.aisbookmarkslist')
        attr = state.attributes
        track = attr.get(bookmark_id)
        d = hass.data[DOMAIN]
        d.async_remove_bookmark(track['id'], True)

    @asyncio.coroutine
    def delete_favorite_service(call):
        """Delete selected favorite"""
        favorite_id = int(call.data.get("id"))
        state = hass.states.get('sensor.aisfavoriteslist')
        attr = state.attributes
        track = attr.get(favorite_id)
        d = hass.data[DOMAIN]
        d.async_remove_bookmark(track['id'], False)

    data = hass.data[DOMAIN] = BookmarksData(hass)
    intent.async_register(hass, AddFavoriteIntent())
    intent.async_register(hass, AddBookmarkIntent())
    intent.async_register(hass, ListTopBookmarkIntent())
    intent.async_register(hass, PlayLastBookmarkIntent())
    hass.services.async_register(DOMAIN, SERVICE_ADD_BOOKMARK, add_bookmark_service)
    hass.services.async_register(DOMAIN, SERVICE_ADD_FAVORITE, add_favorite_service)
    hass.services.async_register(DOMAIN, SERVICE_GET_BOOKMARKS, get_bookmarks_service)
    hass.services.async_register(DOMAIN, SERVICE_GET_FAVORITES, get_favorites_service)
    hass.services.async_register(DOMAIN, SERVICE_PLAY_BOOKMARK, play_bookmark_service)
    hass.services.async_register(DOMAIN, SERVICE_PLAY_FAVORITE, play_favorite_service)
    hass.services.async_register(DOMAIN, SERVICE_DELETE_BOOKMARK, delete_bookmark_service)
    hass.services.async_register(DOMAIN, SERVICE_DELETE_FAVORITE, delete_favorite_service)

    hass.components.conversation.async_register(INTENT_ADD_FAVORITE, [
        'Dodaj do ulubionych',
        'Do ulubionych',
        'Lubię to',
    ])
    hass.components.conversation.async_register(INTENT_ADD_BOOKMARK, ['Dodaj zakładkę', 'Zakładka'])
    hass.components.conversation.async_register(INTENT_LAST_BOOKMARKS, [
        'Jakie mam zakładki', 'Jakie są zakładki'
    ])
    hass.components.conversation.async_register(INTENT_PLAY_LAST_BOOKMARK, [
        'Włącz ostatnią zakładkę', 'Włącz zakładkę', 'Ostatnia zakładka'
    ])

    yield from data.async_load()

    hass.states.async_set("sensor.aisbookmarkslist", -1, {})
    hass.states.async_set("sensor.aisfavoriteslist", -1, {})

    return True


class BookmarksData:
    """Class to hold bookmarks list data."""

    def __init__(self, hass):
        """Initialize the bookmarks list."""
        self.hass = hass
        self.bookmarks = []
        self.favorites = []

    @callback
    def async_add(self, call, bookmark):
        """Add a item."""
        voice_call = False
        if 'voice_call' in call.data:
            voice_call = True

        if bookmark and "attr" not in call.data:
            # ask the player to add bookmark
            self.hass.async_add_job(
                self.hass.services.async_call(
                    'ais_ai_service', 'publish_command_to_frame', {"key": 'addBookmark', "val": True})
            )
            self.hass.async_add_job(
                self.hass.services.async_call(
                    'ais_ai_service', 'say_it', {"text": "Zakładka"})
            )
            return

        attributes = {}
        if "attr" in call.data:
            attributes = call.data["attr"]
            name = attributes.get("media_title").strip()
            source = attributes.get("source").strip()
            media_position = attributes.get("media_position", 0)
            media_content_id = attributes.get("media_content_id")
        else:
            state = self.hass.states.get(ais_global.G_LOCAL_EXO_PLAYER_ENTITY_ID)
            attributes = state.attributes
            name = attributes.get("media_title")
            source = attributes.get("source")
            media_position = attributes.get("media_position", 0)
            media_content_id = attributes.get("media_content_id")
        try:
            media_stream_image = ais_global.G_CURR_MEDIA_CONTENT["IMAGE_URL"]
        except Exception:
            media_stream_image = "/static/icons/favicon-100x100.png"

        try:
            audio_type = ais_global.G_CURR_MEDIA_CONTENT["audio_type"]
        except Exception:
            audio_type = source
        audio_type_pl = ais_global.G_NAME_FOR_AUDIO_NATURE.get(audio_type, audio_type)

        if name is None or source is None or media_content_id is None:
            _LOGGER.warning("can't add the bookmark, no full info provided " + str(attributes))
            return

        if bookmark:
            # type validation
            if audio_type == ais_global.G_AN_RADIO:
                message = "Nie można dodać zakładki do {}".format(audio_type_pl, name)
                self.hass.async_add_job(self.hass.services.async_call('ais_ai_service', 'say_it', {"text": message}))
                return

            #
            full_name = name
            if audio_type == ais_global.G_AN_LOCAL:
                media_content_id = ais_global.G_CURR_MEDIA_CONTENT["lookup_url"]
                full_name = ais_global.G_CURR_MEDIA_CONTENT["ALBUM_NAME"] + " " + name
            # if yt then not bookmark to current search but to lookup url
            elif audio_type == ais_global.G_AN_MUSIC:
                media_content_id = ais_global.G_CURR_MEDIA_CONTENT["lookup_url"]

            # check if the audio is on bookmark list
            item = next((itm for itm in self.bookmarks if (itm['media_content_id'] == media_content_id)), None)
            if item is not None:
                # delete the old bookmark
                self.async_remove_bookmark(item['id'], True)
                message = "Przesuwam zakładkę {}".format(full_name)
            else:
                message = "Dodaję nową zakładkę {}".format(full_name)

            # add the bookmark
            item = {
                'name': full_name,
                'id': uuid.uuid4().hex,
                'source': audio_type,
                'media_position': media_position,
                'media_content_id': media_content_id,
                'media_stream_image': media_stream_image
            }
            self.bookmarks.append(item)
            self.hass.async_add_job(self.save, True)
            if voice_call:
                self.hass.async_add_job(self.hass.services.async_call('ais_ai_service', 'say_it', {"text": message}))
            else:
                _LOGGER.info(message)
            return item
        else:
            # validation
            if source == ais_global.G_AN_FAVORITE:
                message = "{}, {} jest już w ulubionych.".format(audio_type_pl, name)
                self.hass.async_add_job(self.hass.services.async_call('ais_ai_service', 'say_it', {"text": message}))
                return
            # if podcast then we will add not track but audition
            if audio_type == ais_global.G_AN_PODCAST:
                media_content_id = ais_global.G_CURR_MEDIA_CONTENT["lookup_url"]
                name = ais_global.G_CURR_MEDIA_CONTENT["lookup_name"]
            elif audio_type == ais_global.G_AN_MUSIC:
                media_content_id = ais_global.G_CURR_MEDIA_CONTENT["lookup_url"]

            # check if the audio is on favorites list
            item = next((itm for itm in self.favorites if (itm['name'] == name and itm['source'] == audio_type)), None)
            if item is not None:
                message = '{}, {} jest już w ulubionych.'.format(audio_type_pl, name)
                self.hass.async_add_job(self.hass.services.async_call('ais_ai_service', 'say_it', {"text": message}))
                return
            # add item
            item = {
                'name': name,
                'id': uuid.uuid4().hex,
                'source': source,
                'media_content_id': media_content_id,
                'media_stream_image': media_stream_image
            }
            self.favorites.append(item)
            self.hass.async_add_job(self.save, False)
            message = "Dobrze zapamiętam - dodaje {} {} do Twoich ulubionych".format(audio_type_pl, name)
            self.hass.async_add_job(self.hass.services.async_call('ais_ai_service', 'say_it', {"text": message}))
            return item

    @callback
    def async_update(self, item_id, info):
        """Update a bookmarks list item."""
        item = next((itm for itm in self.bookmarks if itm['id'] == item_id), None)

        if item is None:
            raise KeyError

        item.update(info)
        self.hass.async_add_job(self.save)
        return item

    @callback
    def async_remove_bookmark(self, item_id, bookmark):
        if bookmark:
            """Reemove bookmark """
            self.bookmarks = [itm for itm in self.bookmarks if not itm['id'] == item_id]
            self.hass.async_add_job(self.save, True)
        else:
            """Reemove favorites """
            self.favorites = [itm for itm in self.favorites if not itm['id'] == item_id]
            self.hass.async_add_job(self.save, False)


    @asyncio.coroutine
    def async_load(self):
        """Load bookmarks."""
        def load():
            """Load the bookmarks synchronously."""
            try:
                self.bookmarks = load_json(self.hass.config.path(PERSISTENCE_BOOKMARKS), default=[])
                self.favorites = load_json(self.hass.config.path(PERSISTENCE_FAVORITES), default=[])
            except Exception as e:
                _LOGGER.error("Can't load bookmarks data: " + str(e))
        yield from self.hass.async_add_job(load)

    def save(self, bookmark):
        """Save the bookmarks and favorites."""
        if bookmark:
            self.bookmarks = self.bookmarks[-50:]
            save_json(self.hass.config.path(PERSISTENCE_BOOKMARKS), self.bookmarks)
            self.hass.async_add_job(self.hass.services.async_call(DOMAIN, SERVICE_GET_BOOKMARKS))
        else:
            self.favorites = self.favorites[-50:]
            save_json(self.hass.config.path(PERSISTENCE_FAVORITES), self.favorites)
            self.hass.async_add_job(self.hass.services.async_call(DOMAIN, SERVICE_GET_FAVORITES))


class AddFavoriteIntent(intent.IntentHandler):
    """Handle AddItem intents."""
    intent_type = INTENT_ADD_FAVORITE

    @asyncio.coroutine
    def async_handle(self, intent_obj):
        """Handle the intent."""
        hass = intent_obj.hass
        state = hass.states.get('media_player.wbudowany_glosnik')
        name = state.attributes.get("media_title")
        source = state.attributes.get("source")
        media_content_id = state.attributes.get("media_content_id")
        # check if all fields are provided
        if name is None or source is None or media_content_id is None:
            answer = "Nie można dodać do ulubionych - brak informacji o odtwarzanym audio."
        else:
            answer = "Dobrze zapamiętam - dodaje {} do Twoich ulubionych".format(name)
            # hass.data[DOMAIN].async_add(state.attributes, False)
            yield from hass.services.async_call('ais_bookmarks', 'add_favorite', {'voice_call': True})
        response = intent_obj.create_response()
        response.async_set_speech(answer)
        return response


class AddBookmarkIntent(intent.IntentHandler):
    """Handle AddItem intents."""
    intent_type = INTENT_ADD_BOOKMARK

    @asyncio.coroutine
    def async_handle(self, intent_obj):
        """Handle the intent."""
        hass = intent_obj.hass
        state = hass.states.get('media_player.wbudowany_glosnik')
        name = state.attributes.get("media_title")
        source = state.attributes.get("source")
        media_content_id = state.attributes.get("media_content_id")
        # check if all fields are provided
        if name is None or source is None or media_content_id is None:
            answer = "Nie można dodać zakładki - brak informacji o odtwarzanym audio."
        else:
            answer = "Dobrze dodaję zakładkę {}".format(name)
            yield from hass.services.async_call('ais_bookmarks', 'add_bookmark', {'voice_call': True})
        response = intent_obj.create_response()
        response.async_set_speech(answer)
        return response


class ListTopBookmarkIntent(intent.IntentHandler):
    """Handle ListTopBookmarkIntent intents."""

    intent_type = INTENT_LAST_BOOKMARKS

    @asyncio.coroutine
    def async_handle(self, intent_obj):
        """Handle the intent."""
        hass = intent_obj.hass
        bookmarks = intent_obj.hass.data[DOMAIN].bookmarks[-5:]
        response = intent_obj.create_response()
        if not bookmarks:
            answer = "Nie ma żadnych zakładek"
        else:
            answer = "Oto ostatnie {} zakładki na twojej liście: {}".format(
                        min(len(bookmarks), 5),
                        ', '.join(itm['name'] for itm in reversed(bookmarks)))
        response.async_set_speech(answer)
        yield from hass.services.async_call('ais_ai_service', 'say_it', {"text": answer})
        return response


class PlayLastBookmarkIntent(intent.IntentHandler):
    """Handle PlayLastBookmarkIntent intents."""

    intent_type = INTENT_PLAY_LAST_BOOKMARK

    @asyncio.coroutine
    def async_handle(self, intent_obj):
        """Handle the intent."""
        hass = intent_obj.hass
        bookmarks = intent_obj.hass.data[DOMAIN].bookmarks[-1:]
        response = intent_obj.create_response()
        if not bookmarks:
            answer = "Nie ma żadnych zakładek"
        else:
            bookmark = bookmarks[0]["source"] + '; ' + bookmarks[0]["name"]
            answer = "Włączam ostatnią zakładkę {}".format(bookmark)
            yield from hass.services.async_call('ais_bookmarks', 'play_bookmark', {"bookmark": bookmark})
        response.async_set_speech(answer)
        yield from hass.services.async_call('ais_ai_service', 'say_it', {"text": answer})
        return response
