# Copyright (C) 2019  Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from urllib.parse import urljoin

from django.core.cache import cache
from django.db import models
from django.utils.translation import gettext_lazy as _

from passerelle.base.models import BaseResource
from passerelle.utils.api import endpoint
from passerelle.utils.http_authenticators import HttpBearerAuth
from passerelle.utils.jsonresponse import APIError

DEMAND_SCHEMA = {
    '$schema': 'http://json-schema.org/draft-04/schema#',
    'title': 'KIMOCE',
    'description': '',
    'type': 'object',
    'required': ['category', 'type', 'subtype', 'form_url', 'first_name', 'last_name', 'email'],
    'properties': {
        'category': {
            'description': 'demand category',
            'type': 'string',
        },
        'type': {
            'description': 'demand type',
            'type': 'string',
        },
        'subtype': {
            'description': 'demand sub type',
            'type': 'string',
        },
        'form_url': {
            'description': 'form url',
            'type': 'string',
        },
        'first_name': {
            'description': 'user first name',
            'type': 'string',
        },
        'last_name': {
            'description': 'user last name',
            'type': 'string',
        },
        'email': {
            'description': 'user email',
            'type': 'string',
        },
        'priorityId': {
            'description': 'demand priority',
            'type': 'integer',
        },
        'city': {
            'description': 'demand city',
            'type': 'string',
        },
        'zipcode': {
            'description': 'demand zipcode',
            'type': 'string',
        },
        'street_number': {
            'description': 'demand street number',
            'type': 'string',
        },
        'street_name': {
            'description': 'demand street name',
            'type': 'string',
        },
        'lat': {
            'description': 'demand latitude',
            'type': 'string',
        },
        'lon': {
            'description': 'demand longitude',
            'type': 'string',
        },
        'picture1': {
            'description': 'first picture data',
        },
        'picture2': {
            'description': 'second picture data',
        },
        'comment': {
            'description': 'demand comment',
            'type': 'string',
        },
    },
}


class Kimoce(BaseResource):
    base_url = models.URLField(
        max_length=256, blank=False, verbose_name=_('Base URL'), help_text=_('API base URL')
    )
    username = models.CharField(max_length=128, verbose_name=_('Username'))
    password = models.CharField(max_length=128, verbose_name=_('Password'))

    category = _('Business Process Connectors')

    class Meta:
        verbose_name = 'Lille Kimoce'

    @classmethod
    def get_verbose_name(cls):
        return cls._meta.verbose_name

    def check_status(self):
        url = urljoin(self.base_url, 'login_check')
        response = self.requests.post(url, json={'username': self.username, 'password': self.password})
        response.raise_for_status()

    def get_token(self, renew=False):
        token_key = 'lille-kimoce-%s-token' % self.id
        if not renew and cache.get(token_key):
            return cache.get(token_key)
        url = urljoin(self.base_url, 'login_check')
        response = self.requests.post(url, json={'username': self.username, 'password': self.password})
        if not response.status_code // 100 == 2:
            raise APIError(response.content)
        token = response.json()['token']
        cache.set(token_key, token, 3600)
        return token

    def get_referential(self, endpoint, params=None):
        url = urljoin(self.base_url, endpoint)
        data = []
        response = self.requests.get(url, params=params, auth=HttpBearerAuth(self.get_token()))
        if response.status_code == 401:
            response = self.requests.get(url, params=params, auth=HttpBearerAuth(self.get_token(renew=True)))
        if response.status_code // 100 == 2:
            for member in response.json()['hydra:member']:
                member['number'] = member['id']
                member['text'] = member['label']
                member['id'] = member['@id']
                data.append(member)
        return {'data': data}

    @endpoint(perm='can_access', description=_('List categories'))
    def categories(self, request, *args, **kwargs):
        return self.get_referential('categories')

    @endpoint(perm='can_access', description=_('List types'))
    def types(self, request, category_id):
        return self.get_referential('types', {'category.id': category_id})

    @endpoint(perm='can_access', description=_('List subtypes'))
    def subtypes(self, request, type_id):
        return self.get_referential('sub_types', {'types.id': type_id})

    @endpoint(perm='can_access', description=_('List streets'))
    def streets(self, request, **kwargs):
        url = urljoin(self.base_url, 'company_locations')
        data = []
        params = {'road': 'true'}
        if 'id' in kwargs:
            params['streetAddress'] = kwargs['id']
        if 'q' in kwargs:
            params['streetAddress'] = kwargs['q']
        # parentId is a flag to filter street names only
        response = self.requests.get(url, params=params, auth=HttpBearerAuth(self.get_token()))
        if response.status_code == 401:
            response = self.requests.get(url, params=params, auth=HttpBearerAuth(self.get_token(renew=True)))
        if response.status_code // 100 == 2:
            for street in response.json()['hydra:member']:
                street['number'] = street['@id']
                street['id'] = street['@id']
                street['text'] = street['streetAddress']
                data.append(street)
        return {'data': data}

    @endpoint(
        perm='can_access',
        description=_('Create demand'),
        post={
            'description': _('Create demand into KIMOCE'),
            'request_body': {'schema': {'application/json': DEMAND_SCHEMA}},
        },
    )
    def create_demand(self, request, post_data):
        payload = {
            'category': post_data['category'],
            'type': post_data['type'],
            'subType': post_data['subtype'],
            'priorityId': post_data.get('priorityId', 3),
            'companyLocation': {
                'number': post_data.get('street_number', ''),
                'road': post_data.get('street_name', ''),
                'city': post_data.get('city', ''),
                'zipCode': post_data.get('zipcode', ''),
            },
            'sourceContact': {
                'firstname': post_data['first_name'],
                'lastname': post_data['last_name'],
                'mail': post_data['email'],
            },
            'pictures': [],
            'GRUResponseLink': post_data['form_url'],
        }
        if post_data.get('lat') and post_data.get('lon'):
            payload['coordinate'] = {'latitude': post_data['lat'], 'longitude': post_data['lon']}
        for param_name in ('picture1', 'picture2'):
            if (
                post_data.get(param_name)
                and isinstance(post_data[param_name], dict)
                and post_data[param_name].get('content')
            ):
                payload['pictures'].append({'content': post_data[param_name]['content']})
        if post_data.get('comment'):
            payload['comment'] = {'content': post_data['comment']}
        url = urljoin(self.base_url, 'demands')
        result = self.requests.post(url, json=payload, auth=HttpBearerAuth(self.get_token()))
        if result.status_code == 401:
            result = self.requests.post(url, json=payload, auth=HttpBearerAuth(self.get_token(renew=True)))
        if result.status_code // 100 == 2:
            return {'data': result.json()}
        raise APIError(result.content)
