import time

try:
    from collections import Iterable
except:
    from collections.abc import Iterable

from saas.library.python.abc.enums import ResourceState

from saas.library.python.api_mixins import JsonAPI
from saas.library.python.token_store import PersistentTokenStore


class AbcAPI(JsonAPI):
    def __init__(self, base_url='https://abc-back.yandex-team.ru/api/v4', abc_token=None):
        if not abc_token:
            abc_token = PersistentTokenStore.get_token_from_store_env_or_file('abc')

        headers = {
            'Authorization': 'OAuth {token}'.format(token=abc_token),
            'Content-Type': 'application/json; charset=UTF-8'
        }

        super(AbcAPI, self).__init__(base_url, headers=headers)

        self.resource_type_mapping = {}

    @staticmethod
    def _is_error_response(response_data):
        return not isinstance(response_data, dict) or response_data.get('error')

    @staticmethod
    def _get_error_from_response_data(response_data):
        return response_data.get('error', {}).get('code')

    def get_resources(self, external_id):
        params = {'external_id': external_id}
        return self._make_request('get', 'resources/', params=params)['results']

    def get_resources_consumers(
        self,
        service_id=None,
        resource_type_id=None,
        resource_external_id=None,
        state=(ResourceState.requested, ResourceState.approved, ResourceState.granting, ResourceState.granted),
        request_id=None,
        with_pagination=False
    ):
        params = {
            'service': service_id,
            'type': resource_type_id,
            'resource__external_id': resource_external_id,
            'request_id': request_id
        }

        if isinstance(state, Iterable):
            params['state__in'] = [s.value for s in state]
        elif state:
            params['state'] = state.value

        response_data = self._make_request('get', 'resources/consumers/', params=params)
        return response_data if with_pagination else response_data['results']

    def get_resources_consumers_meta_info(self, resource_consumer_id):
        params = {'action': 'meta_info'}
        return self._make_request(
            'post',
            'resources/consumers/{resource_consumer_id}/actions/'.format(resource_consumer_id=resource_consumer_id),
            json_params=params
        )['result']

    def get_services(self, service_id):
        return self._make_request('get', 'services/{id}/'.format(id=service_id))

    def get_resource_types(self, service_id=None, name_contains=None):
        params = {
            'service_id': service_id,
            'name__contains': name_contains
        }

        return self._make_request('get', 'resources/types/', params=params)

    def get_resource_type_id(self, service_id=None, name_contains=None):
        params_tpl = (service_id, name_contains)

        if params_tpl not in self.resource_type_mapping:
            resources = self.get_resource_types(service_id=service_id, name_contains=name_contains)
            resource_type_ids = set([t['id'] for t in resources['results']])

            if len(resource_type_ids) == 1:
                self.resource_type_mapping[params_tpl] = resource_type_ids.pop()
            else:
                raise RuntimeError('Multiple resource types for {params}'.format(params=params_tpl))

        return self.resource_type_mapping[params_tpl]

    def get_tvm_meta_info(self, tvm_id, abc_service_id):
        resources_consumers = self.get_resources_consumers(
            service_id=abc_service_id,
            resource_external_id=tvm_id
        )
        cnt = len(resources_consumers)
        if cnt != 1:
            raise RuntimeError('Unable to find exactly one abc resource consumer for tvm {tvm_id}, found: {cnt}'.format(
                tvm_id=tvm_id,
                cnt=cnt
            ))
        resource_consumer = resources_consumers[0]
        return self.get_resources_consumers_meta_info(resource_consumer_id=resource_consumer['id'])

    def get_tvm_applications(self, service_id, request_id=None):
        tvm_app_resource_type = self.get_resource_type_id(name_contains='TVM', service_id=service_id)
        current_page = self.get_resources_consumers(
            resource_type_id=tvm_app_resource_type,
            service_id=service_id,
            request_id=request_id,
            with_pagination=True
        )

        while True:
            next_page = current_page['next']
            for result in current_page['results']:
                yield result
            if next_page:
                current_page = self._session.get(next_page).json()
            else:
                break

    def create_tvm_app(self, abc_service_id, tvm_application_name):
        result = self._make_request('post', 'resources/request/', json_params={
            'service': abc_service_id,
            'resource_type': self.get_resource_type_id(name_contains='TVM'),
            'data': {'resource_name': tvm_application_name}
        })

        while True:
            tvm_apps = list(self.get_tvm_applications(
                abc_service_id,
                request_id=result['answer_id'])
            )
            time.sleep(1)
            if tvm_apps:
                return tvm_apps[0]

    def get_or_create_searchproxy_tvm_application(self, installation_name, contour_type, abc_service_id=664):
        tvm_app_name = 'SearchProxy {}/{} TVM application'.format(installation_name, contour_type)
        tvm_app_resource = None
        for tvm_app in self.get_tvm_applications(abc_service_id):
            if tvm_app['resource']['name'] == tvm_app_name:
                tvm_app_resource = tvm_app
        if tvm_app_resource is None:
            tvm_app_resource = self.create_tvm_app(abc_service_id, tvm_app_name)
        return tvm_app_resource
