"""Module provider for Zeit"""
from __future__ import absolute_import
import json
import logging

import requests
from lexicon.providers.base import Provider as BaseProvider


LOGGER = logging.getLogger(__name__)

NAMESERVER_DOMAINS = ['zeit.world']


def provider_parser(subparser):
    """Configure provider parser for Zeit"""
    subparser.description = '''
        Zeit Provider requires a token to access its API.
        You can generate one for your account on the following URL:
        https://zeit.co/account/tokens'''
    subparser.add_argument('--auth-token', help='specify your API token')


class Provider(BaseProvider):
    """
    Implements the DNS Zeit provider.
    The API is quite simple: you can list all records, add one record or delete one record.
        - list is pretty straightforward: we get all records then filter for given parameters,
        - add uses directly the API to add a new record without any added complexity,
        - delete uses list + delete: we get the list of all records,
          filter on the given parameters and delete record by id,
        - update uses list + delete + add: we get the list of all records,
          find record for given identifier, then insert a new record and delete the old record.
    """

    def __init__(self, config):
        super(Provider, self).__init__(config)
        self.domain_id = None
        self.api_endpoint = 'https://api.zeit.co/v2/domains'

    def _authenticate(self):
        result = self._get('/{0}'.format(self.domain))

        if not result['uid']:
            raise Exception('Error, domain {0} not found'.format(self.domain))

        self.domain_id = result['uid']

    def _list_records(self, rtype=None, name=None, content=None):
        result = self._get('/{0}/records'.format(self.domain))

        raw_records = result['records']
        if rtype:
            raw_records = [
                raw_record for raw_record in raw_records
                if raw_record['type'] == rtype]
        if name:
            raw_records = [
                raw_record for raw_record in raw_records
                if raw_record['name'] == self._relative_name(name)]
        if content:
            raw_records = [
                raw_record for raw_record in raw_records
                if raw_record['value'] == content]

        records = []
        for raw_record in raw_records:
            records.append({
                'id': raw_record['id'],
                'type': raw_record['type'],
                'name': self._full_name(raw_record['name']),
                'content': raw_record['value']
            })

        LOGGER.debug('list_records: %s', records)

        return records

    def _create_record(self, rtype, name, content):
        # We ignore creation if a record already exists for given rtype/name/content
        records = self._list_records(rtype, name, content)
        if records:
            LOGGER.debug('create_record (ignored, duplicate): %s',
                         records[0]['id'])
            return True

        data = {
            'type': rtype,
            'name': self._relative_name(name),
            'value': content
        }

        result = self._post('/{0}/records'.format(self.domain), data)

        if not result['uid']:
            raise Exception('Error occured when inserting the new record.')

        LOGGER.debug('create_record: %s', result['uid'])

        return True

    def _update_record(self, identifier, rtype=None, name=None, content=None):
        # Zeit do not allow to update a record, only add or remove.
        # So we get the corresponding record, dump or update
        # its content and insert it as a new record.
        # Then we remove the old record.
        records = []
        if identifier:
            records = self._list_records()
            records = [
                record for record in records if record['id'] == identifier]
        else:
            records = self._list_records(rtype, name)

        if not records:
            raise Exception(
                'No record found for identifer: {0}'.format(identifier))

        if len(records) > 1:
            LOGGER.warning(
                'Multiple records have been found for given parameters. '
                'Only first one will be updated (id: %s)', records[0]['id'])

        data = {
            'type': rtype,
            'name': self._relative_name(name),
            'value': content
        }

        if not rtype:
            data['type'] = records[0]['type']
        if not name:
            data['name'] = self._relative_name(records[0]['name'])
        if not content:
            data['value'] = records[0]['content']

        result = self._post('/{0}/records'.format(self.domain), data)
        self._delete('/{0}/records/{1}'.format(self.domain, records[0]['id']))

        LOGGER.debug('update_record: %s => %s',
                     records[0]['id'], result['uid'])

        return True

    def _delete_record(self, identifier=None, rtype=None, name=None, content=None):
        delete_record_ids = []
        if not identifier:
            records = self._list_records(rtype, name, content)
            delete_record_ids = [record['id'] for record in records]
        else:
            delete_record_ids.append(identifier)

        LOGGER.debug('delete_records: %s', delete_record_ids)

        for delete_record_id in delete_record_ids:
            self._delete(
                '/{0}/records/{1}'.format(self.domain, delete_record_id))

        LOGGER.debug('delete_record: %s', True)

        return True

    def _request(self, action='GET', url='/', data=None, query_params=None):
        if data is None:
            data = {}
        if query_params is None:
            query_params = {}

        request = requests.request(
            action,
            self.api_endpoint + url,
            params=query_params,
            data=json.dumps(data),
            headers={
                'Authorization': 'Bearer {0}'.format(
                    self._get_provider_option('auth_token'))})

        request.raise_for_status()
        return request.json()
