"""ACME AuthHandler."""
import itertools
import logging
import time

import zope.component

from acme import challenges
from acme import messages

from letsencrypt import achallenges
from letsencrypt import constants
from letsencrypt import errors
from letsencrypt import error_handler
from letsencrypt import interfaces


logger = logging.getLogger(__name__)


class AuthHandler(object):
    """ACME Authorization Handler for a client.

    :ivar dv_auth: Authenticator capable of solving
        :class:`~acme.challenges.DVChallenge` types
    :type dv_auth: :class:`letsencrypt.interfaces.IAuthenticator`

    :ivar cont_auth: Authenticator capable of solving
        :class:`~acme.challenges.ContinuityChallenge` types
    :type cont_auth: :class:`letsencrypt.interfaces.IAuthenticator`

    :ivar acme.client.Client acme: ACME client API.

    :ivar account: Client's Account
    :type account: :class:`letsencrypt.account.Account`

    :ivar dict authzr: ACME Authorization Resource dict where keys are domains
        and values are :class:`acme.messages.AuthorizationResource`
    :ivar list dv_c: DV challenges in the form of
        :class:`letsencrypt.achallenges.AnnotatedChallenge`
    :ivar list cont_c: Continuity challenges in the
        form of :class:`letsencrypt.achallenges.AnnotatedChallenge`

    """
    def __init__(self, dv_auth, cont_auth, acme, account):
        self.dv_auth = dv_auth
        self.cont_auth = cont_auth
        self.acme = acme

        self.account = account
        self.authzr = dict()

        # List must be used to keep responses straight.
        self.dv_c = []
        self.cont_c = []

    def get_authorizations(self, domains, best_effort=False):
        """Retrieve all authorizations for challenges.

        :param list domains: Domains for authorization
        :param bool best_effort: Whether or not all authorizations are
             required (this is useful in renewal)

        :returns: tuple of lists of authorization resources. Takes the
            form of (`completed`, `failed`)
        :rtype: tuple

        :raises .AuthorizationError: If unable to retrieve all
            authorizations

        """
        for domain in domains:
            self.authzr[domain] = self.acme.request_domain_challenges(
                domain, self.account.regr.new_authzr_uri)

        self._choose_challenges(domains)

        # While there are still challenges remaining...
        while self.dv_c or self.cont_c:
            cont_resp, dv_resp = self._solve_challenges()
            logger.info("Waiting for verification...")

            # Send all Responses - this modifies dv_c and cont_c
            self._respond(cont_resp, dv_resp, best_effort)

        # Just make sure all decisions are complete.
        self.verify_authzr_complete()
        # Only return valid authorizations
        return [authzr for authzr in self.authzr.values()
                if authzr.body.status == messages.STATUS_VALID]

    def _choose_challenges(self, domains):
        """Retrieve necessary challenges to satisfy server."""