import logging
logging.basicConfig(level=logging.DEBUG)

from threading import Condition
from trakt import Trakt
import os


class Application(object):
    def __init__(self):
        self.is_authenticating = Condition()

        self.authorization = None

        # Bind trakt events
        Trakt.on('oauth.token_refreshed', self.on_token_refreshed)

    def authenticate(self):
        if not self.is_authenticating.acquire(blocking=False):
            print 'Authentication has already been started'
            return False

        # Request new device code
        code = Trakt['oauth/device'].code()

        print 'Enter the code "%s" at %s to authenticate your account' % (
            code.get('user_code'),
            code.get('verification_url')
        )

        # Construct device authentication poller
        poller = Trakt['oauth/device'].poll(**code)\
            .on('aborted', self.on_aborted)\
            .on('authenticated', self.on_authenticated)\
            .on('expired', self.on_expired)\
            .on('poll', self.on_poll)

        # Start polling for authentication token
        poller.start(daemon=False)

        # Wait for authentication to complete
        return self.is_authenticating.wait()

    def run(self):
        self.authenticate()

        if not self.authorization:
            print 'ERROR: Authentication required'
            exit(1)

        # Simulate expired token
        self.authorization['expires_in'] = 0

        # Test authenticated calls
        with Trakt.configuration.oauth.from_response(self.authorization):
            # Expired token, requests will return `None`
            print Trakt['sync/collection'].movies()

        with Trakt.configuration.oauth.from_response(self.authorization, refresh=True):
            # Expired token will be refreshed automatically (as `refresh=True`)
            print Trakt['sync/collection'].movies()

        with Trakt.configuration.oauth.from_response(self.authorization):
            # Current token is still valid
            print Trakt['sync/collection'].movies()

    def on_aborted(self):
        """Triggered when device authentication was aborted (either with `DeviceOAuthPoller.stop()`
           or via the "poll" event)"""

        print 'Authentication aborted'

        # Authentication aborted
        self.is_authenticating.acquire()
        self.is_authenticating.notify_all()
        self.is_authenticating.release()

    def on_authenticated(self, authorization):
        """Triggered when device authentication has been completed

        :param authorization: Authentication token details
        :type authorization: dict
        """

        # Acquire condition
        self.is_authenticating.acquire()

        # Store authorization for future calls
        self.authorization = authorization

        print 'Authentication successful - authorization: %r' % self.authorization

        # Authentication complete
        self.is_authenticating.notify_all()
        self.is_authenticating.release()

    def on_expired(self):
        """Triggered when the device authentication code has expired"""

        print 'Authentication expired'

        # Authentication expired
        self.is_authenticating.acquire()
        self.is_authenticating.notify_all()
        self.is_authenticating.release()

    def on_poll(self, callback):
        """Triggered before each poll

        :param callback: Call with `True` to continue polling, or `False` to abort polling
        :type callback: func
        """

        # Continue polling
        callback(True)

    def on_token_refreshed(self, authorization):
        # OAuth token refreshed, store authorization for future calls
        self.authorization = authorization

        print 'Token refreshed - authorization: %r' % self.authorization


if __name__ == '__main__':
    # Configure
    Trakt.base_url = 'http://api.staging.trakt.tv'

    Trakt.configuration.defaults.client(
        id=os.environ.get('CLIENT_ID'),
        secret=os.environ.get('CLIENT_SECRET')
    )

    app = Application()
    app.run()
