import operator
from datetime import datetime, timedelta

from series.get import ReleaseMonitor, ReleaseHandler
from series.get.search import TorrentSearch
from series.get.model.link import Torrent
from series.handler import Job
from series.condition import LambdaCondition, StrictCondition
from series.get.handler import R

from golgi import configurable

from tek_utils.sharehoster.errors import ShareHosterError

from amino import _, List, Map


class Downgrade(Job):
    pass


class CheckCaching(Job):
    pass


class HasStandardDef(R):

    def __init__(self) -> None:
        super().__init__('resolutions', target='')

    @property
    def _desc(self):
        return 'has sd'

    @property
    def _oper(self):
        return operator.contains

    def _repr(self, item, match: bool) -> str:
        return 'sd' if match else 'no sd'


class Thresh(R):

    def __init__(self, attr) -> None:
        return super().__init__(attr, target=None)

    def _target(self, item):
        return item.downgrade_after_hours

    def _value(self, item):
        return datetime.now() - super()._value(item)

    @property
    def _oper(self):
        return operator.gt

    def _oper_repr(self, match):
        return '>' if match else '>'

    @property
    def _desc(self):
        return '{} old'.format(super()._desc)


class AttrNull(R):

    def __init__(self, attr) -> None:
        super().__init__(attr, target=0)

    @property
    def _desc(self):
        return '{} null'.format(super()._desc)


class AirdateNull(AttrNull):

    def __init__(self) -> None:
        super().__init__('airdate')

    def _value(self, item):
        return item.release.airdate


class AirdateThresh(Thresh):

    def __init__(self) -> None:
        super().__init__('airdate')

    def _value(self, item):
        return datetime.now() - item.release.airdate


class LastUpdateNull(AttrNull):

    def __init__(self) -> None:
        super().__init__('last_torrent_update_stamp')


@configurable(torrent=['search_engine'], get=['min_seeders'])
class TorrentCleaner(ReleaseHandler):

    def __init__(self, releases, *a, **kw):
        super().__init__(releases, 600, 'torrent cleaner', **kw)
        self._search = TorrentSearch(self._search_engine, self._min_seeders)

    @property
    def _candidates(self):
        down = (
            self._no_cached_torrents_q
            .filter(ReleaseMonitor.downgrade_after > 0)
            .all()
        )
        caching = (
            self._no_cached_torrents_q
            .join(Torrent)
            .filter(Torrent.caching)
            .all()
        )
        return ((List.wrap(down) / Downgrade) +
                (List.wrap(caching) / CheckCaching))

    @property
    def _job_conditions(self):
        return Map({
            Downgrade: self._downgrade_conditions,
            CheckCaching: self._check_caching_conditions,
        })

    @property
    def _downgrade_conditions(self):
        return (
            ~HasStandardDef() &
            ~LambdaCondition('alternative hd', self._alternative_hd_releases) &
            (
                (~LastUpdateNull() & Thresh('last_torrent_update')) |
                (LastUpdateNull() & ~AirdateNull() & AirdateThresh())
            )
        )

    @property
    def _check_caching_conditions(self):
        return StrictCondition(True)

    def _handle_job(self, job):
        monitor = job.item
        if isinstance(job, Downgrade):
            return self._downgrade(monitor)
        elif isinstance(job, CheckCaching):
            return self._check_caching(monitor)

    def _downgrade(self, monitor):
        self.log.info('Downgrading resolution of {}'.format(monitor.release))
        self._update(
            monitor,
            _resolutions=','.join(monitor.resolutions.cat('')),
        )
        self._releases.reset_torrent(monitor.id)

    def _alternative_hd_releases(self, monitor):
        return self._search.search(monitor).exists(_.choose.is_just).value

    def _check_caching(self, monitor):
        self._check_pending(monitor)

    def _check_pending(self, monitor):
        msg = 'Checking status of caching torrents for {}'
        self.log.debug(msg.format(monitor.release))
        for link in monitor.torrent_links:
            torrent = link.torrent
            try:
                if not link.cached and torrent.cached:
                    self._releases.torrent_cached(link.id)
                    msg = 'Flagging torrent as cached: {}'
                    self.log.info(msg.format(monitor.release))
                elif not link.dead and not link.cached and not torrent.caching:
                    self._releases.update_link(link.id, caching=False)
                    msg = 'Torrent was canceled remotely: {}'
                    self.log.warning(msg.format(link))
            except ShareHosterError as e:
                msg = 'Error checking torrent status for {}: {}'
                self.log.error(msg.format(monitor.release, e))

__all__ = ('TorrentCleaner',)
