from __future__ import unicode_literals, division, absolute_import
from datetime import datetime
import logging

from sqlalchemy import Column, Integer, String, Unicode, DateTime
from sqlalchemy.schema import Index
from sqlalchemy.sql.expression import desc

from flexget import plugin
from flexget.event import fire_event, event
from flexget.manager import Base
from flexget.utils.log import log_once
from flexget.utils.titles.movie import MovieParser
from flexget.utils.tools import parse_timedelta

log = logging.getLogger('proper_movies')


class ProperMovie(Base):

    __tablename__ = 'proper_movies'

    id = Column(Integer, primary_key=True)
    title = Column(Unicode)
    task = Column('feed', Unicode)
    imdb_id = Column(String, index=True)
    quality = Column(String)
    proper_count = Column(Integer)
    added = Column(DateTime)

    def __init__(self):
        self.added = datetime.now()

    def __repr__(self):
        return '<ProperMovie(title=%s,task=%s,imdb_id=%s,quality=%s,proper_count=%s,added=%s)>' % \
            (self.title, self.task, self.imdb_id, self.quality, self.proper_count, self.added)


# create index
columns = Base.metadata.tables['proper_movies'].c
Index('proper_movies_imdb_id_quality_proper', columns.imdb_id, columns.quality, columns.proper_count)


class FilterProperMovies(object):
    """
        Automatically download proper movies.

        Configuration:

            proper_movies: n <minutes|hours|days|weeks>

        or permanently:

            proper_movies: yes

        Value no will disable plugin.
    """

    schema = {
        'oneOf': [
            {'type': 'boolean'},
            {'type': 'string', 'format': 'interval'}
        ]
    }

    def on_task_filter(self, task, config):
        log.debug('check for enforcing')

        # parse config
        if isinstance(config, bool):
            # configured a boolean false, disable plugin
            if not config:
                return
            # configured a boolean true, disable timeframe
            timeframe = None
        else:
            # parse time window
            log.debug('interval: %s' % config)
            try:
                timeframe = parse_timedelta(config)
            except ValueError:
                raise plugin.PluginError('Invalid time format', log)

        # throws DependencyError if not present aborting task
        imdb_lookup = plugin.get_plugin_by_name('imdb_lookup').instance

        for entry in task.entries:

            parser = MovieParser()
            parser.data = entry['title']
            parser.parse()

            # if we have imdb_id already evaluated
            if entry.get('imdb_id', None, eval_lazy=False) is None:
                try:
                    # TODO: fix imdb_id_lookup, cumbersome that it returns None and or throws exception
                    # Also it's crappy name!
                    imdb_id = imdb_lookup.imdb_id_lookup(movie_title=parser.name, raw_title=entry['title'])
                    if imdb_id is None:
                        continue
                    entry['imdb_id'] = imdb_id
                except plugin.PluginError as pe:
                    log_once(pe.value)
                    continue

            quality = parser.quality.name

            log.debug('quality: %s' % quality)
            log.debug('imdb_id: %s' % entry['imdb_id'])
            log.debug('current proper count: %s' % parser.proper_count)

            proper_movie = task.session.query(ProperMovie).\
                filter(ProperMovie.imdb_id == entry['imdb_id']).\
                filter(ProperMovie.quality == quality).\
                order_by(desc(ProperMovie.proper_count)).first()

            if not proper_movie:
                log.debug('no previous download recorded for %s' % entry['imdb_id'])
                continue

            highest_proper_count = proper_movie.proper_count
            log.debug('highest_proper_count: %i' % highest_proper_count)

            accept_proper = False
            if parser.proper_count > highest_proper_count:
                log.debug('proper detected: %s ' % proper_movie)

                if timeframe is None:
                    accept_proper = True
                else:
                    expires = proper_movie.added + timeframe
                    log.debug('propers timeframe: %s' % timeframe)
                    log.debug('added: %s' % proper_movie.added)
                    log.debug('propers ignore after: %s' % str(expires))
                    if datetime.now() < expires:
                        accept_proper = True
                    else:
                        log.verbose('Proper `%s` has past it\'s expiration time' % entry['title'])

            if accept_proper:
                log.info('Accepting proper version previously downloaded movie `%s`' % entry['title'])
                # TODO: does this need to be called?
                # fire_event('forget', entry['imdb_url'])
                fire_event('forget', entry['imdb_id'])
                entry.accept('proper version of previously downloaded movie')

    def on_task_learn(self, task, config):
        """Add downloaded movies to the database"""
        log.debug('check for learning')
        for entry in task.accepted:
            if 'imdb_id' not in entry:
                log.debug('`%s` does not have imdb_id' % entry['title'])
                continue

            parser = MovieParser()
            parser.data = entry['title']
            parser.parse()

            quality = parser.quality.name

            log.debug('quality: %s' % quality)
            log.debug('imdb_id: %s' % entry['imdb_id'])
            log.debug('proper count: %s' % parser.proper_count)

            proper_movie = task.session.query(ProperMovie).\
                filter(ProperMovie.imdb_id == entry['imdb_id']).\
                filter(ProperMovie.quality == quality).\
                filter(ProperMovie.proper_count == parser.proper_count).first()

            if not proper_movie:
                pm = ProperMovie()
                pm.title = entry['title']
                pm.task = task.name
                pm.imdb_id = entry['imdb_id']
                pm.quality = quality
                pm.proper_count = parser.proper_count
                task.session.add(pm)
                log.debug('added %s' % pm)
            else:
                log.debug('%s already exists' % proper_movie)

@event('plugin.register')
def register_plugin():
    plugin.register(FilterProperMovies, 'proper_movies', api_ver=2)
