# MIT License
#
# Copyright (c) 2020 Brett Graves
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import weakref
from collections.abc import MutableMapping, MutableSet
from datetime import datetime, timedelta

from .methods import (
    AppendWatchlist,
    CreateWatchlist,
    DeleteFromWatchlist,
    DeleteWatchlist,
    GetWatchlist,
    GetWatchlists,
)


class WatchlistWrapper(MutableSet):
    _name = ""
    _syms = set()

    def __init__(self, parent, name, symbols):
        self._name = name
        self._syms = set(symbols)
        self._auth = weakref.ref(parent._auth())

    def __str__(self):
        return str(self._syms)

    def __iter__(self):
        return self._syms.__iter__()

    def __contains__(self, x):
        return self._syms.contains(x)

    def __len__(self):
        return self._syms.__len__()

    def add(self, x):
        if x not in self._syms:
            result = AppendWatchlist(
                auth=self._auth(), watchlist_name=self._name, watchlist_symbols=x
            ).request()

    def discard(self, x):
        if x in self._syms:
            result = DeleteFromWatchlist(
                auth=self._auth(), watchlist_name=self._name, watchlist_symbol=x
            ).request()


class Watchlist(MutableMapping):
    """Handle an accounts watchlists and symbols in a pythonic way.

            The Watchlist account object wraps ally's watchlist
            functionality and mimics python datatypes.

            Examples:

    .. code-block:: python

            # See all of your watchlists
            list(a.watchlists)

            # => ['w-list1', 'my-watchlist',...]


    .. code-block:: python

            # See all the symbols associated with a watchlist
            list(a.watchlist['w-list1'])

            # => ['aapl, 'googl',...]

    .. code-block:: python

            # Create a watchlist, and initialize with symbols
            a.watchlist['new-watchlist'] = ['aapl,'googl',...]

    .. code-block:: python

            # Remove a symbol from a watchlist
            a.watchlist['new-watchlist'].pop('aapl')

    .. code-block:: python

            # Delete a watchlist
            a.watchlist.pop('new-watchlist')


    """

    _auth = None
    _expire = None

    def __getitem__(self, name):
        result = GetWatchlist(auth=self._auth(), watchlist_name=name).request()

        return WatchlistWrapper(self, name, result)

    def __setitem__(self, name, symbols):
        if type(symbols) != type([]):
            symbols = [str(symbols)]

        CreateWatchlist(
            auth=self._auth(), watchlist_name=name, watchlist_symbols=symbols
        ).request()

    def __delitem__(self, name):
        DeleteWatchlist(auth=self._auth(), watchlist_name=name).request()

    @property
    def _all(self):
        """Reusable way to get all watchlists"""
        t = datetime.now()

        if self._expire is None or self._expire < t:

            # Update cache
            self._expire = t + timedelta(seconds=0.75)

            self._lists = GetWatchlists(
                auth=self._auth(),
            ).request()

        return self._lists

    def __str__(self):
        return str(self._all)

    def __iter__(self):
        """Return list to run ove
        Must be wrapped in some special iterator stuff
        so that python3 will handle it how we want
        """
        return self._all.__iter__()

    def __len__(self):
        return len(self._all)

    def __init__(self, parent):
        self._auth = weakref.ref(parent.auth)
