# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2018 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/>.

import datetime
import json
from urllib import parse as urlparse

from django.db import models
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from requests import RequestException

from passerelle.base.models import BaseResource
from passerelle.utils.api import APIError, endpoint


class StrasbourgEu(BaseResource):
    category = _('Misc')

    liferay_api_url = models.URLField(_('Liferay API URL'), max_length=256)

    class Meta:
        verbose_name = _('Strasbourg.eu')

    def check_status(self):
        url = urlparse.urljoin(self.liferay_api_url, 'jsonws/interest.interest/get-interests')
        response = self.requests.get(url)
        response.raise_for_status()
        response.json()['interests']  # pylint: disable=expression-not-assigned

    @endpoint(
        perm='can_access',
        methods=['get', 'post'],
        description_get=_('List interests'),
        description_post=_('Update interests'),
    )
    def interests(self, request, name_id=None, **kwargs):
        if request.method == 'POST':
            if name_id is None:
                raise APIError('missing name_id')
            # expected content: {"interests": ["123", "456"]}
            response = json.loads(request.body)
            if 'error ' in response:
                return {'err': 1, 'err_desc': response.get('error')}
            interests = response.get('interests')
            if interests is None:
                interests = []  # reset
            url = urlparse.urljoin(self.liferay_api_url, 'jsonws/interest.interest/set-user-interests')
            try:
                resp = self.requests.post(url, data={'userId': name_id, 'interestIds': ','.join(interests)})
                resp.raise_for_status()
                response = resp.json()
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            if 'error' in response:
                return {'err': 1, 'err_desc': response.get('error')}
        url = urlparse.urljoin(self.liferay_api_url, 'jsonws/interest.interest/get-interests')
        try:
            resp = self.requests.get(url)
            resp.raise_for_status()
            response = resp.json()
        except (ValueError, RequestException):
            return {'err': 2, 'err_desc': 'invalid service answer'}
        interests = response.get('interests')
        if interests is None:
            return {'err': 1, 'err_desc': response.get('error')}
        if name_id is not None:
            url = urlparse.urljoin(self.liferay_api_url, 'jsonws/interest.interest/get-user-interests')
            try:
                resp = self.requests.post(url, data={'userId': name_id})
                resp.raise_for_status()
                user_choices = resp.json().get('interests')
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            interests = [x for x in interests if x['id'] in user_choices]
        for interest in interests:
            interest['text'] = '%s / %s' % (interest['type'], interest['name'])
        interests.sort(key=lambda x: x['text'])
        return {'data': interests}

    @endpoint(
        perm='can_access',
        methods=['get', 'post'],
        description_get=_('List notifications'),
        description_post=_('Add notification'),
    )
    def notifications(self, request, name_id, **kwargs):
        if request.method == 'GET':
            url = urlparse.urljoin(
                self.liferay_api_url, 'jsonws/notification.notification/get-user-notifications'
            )
            try:
                resp = self.requests.post(url, data={'userId': name_id})
                resp.raise_for_status()
                notifications = resp.json()
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            if 'error' in notifications:
                return {'err': 1, 'err_desc': notifications.get('error')}
            for notification in notifications['notifications']:
                notification['parsedPublicationDate'] = None
                for date_format in ('%Y-%m-%d %H:%M:%S', '%a %b %d %H:%M:%S %Z %Y'):
                    try:
                        notification['parsedPublicationDate'] = force_str(
                            datetime.datetime.strptime(notification['publicationDate'], date_format)
                        )
                        break
                    except ValueError:
                        pass
                else:
                    self.logger.warning(
                        'received invalid publicationDate for notification %r: %r',
                        notification['id'],
                        notification['publicationDate'],
                    )
            notifications['notifications'] = [
                x for x in notifications['notifications'] if x['parsedPublicationDate']
            ]
            notifications['notifications'].sort(key=lambda x: x['parsedPublicationDate'], reverse=True)
            return notifications
        else:
            # expected content: {"title": ..., "description": ..., "url": ...,
            # ...}, cf https://strasbourgeurometropole.github.io/slate/#ajout-d-39-une-notification
            notification = json.loads(request.body)
            notification['userId'] = name_id
            url = urlparse.urljoin(self.liferay_api_url, 'jsonws/notification.notification/add-notification')
            try:
                resp = self.requests.post(url, data=notification)
                resp.raise_for_status()
                response = resp.json()
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            if response.get('success'):
                return {'err': 0, 'err_desc': response.get('success')}
            else:
                return {'err': 1, 'err_desc': response.get('error')}

    @endpoint(
        perm='can_access',
        methods=['get', 'post'],
        description_get=_('List favorites'),
        description_post=_('Add favorite'),
    )
    def favorites(self, request, name_id, url_filter=None, **kwargs):
        if request.method == 'GET':
            url = urlparse.urljoin(self.liferay_api_url, 'jsonws/favorite.favorite/get-user-favorites')
            try:
                resp = self.requests.post(url, data={'userId': name_id})
                resp.raise_for_status()
                response = resp.json()
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            if 'error' in response:
                return {'err': 1, 'err_desc': response.get('error')}
            favorites = response['favorites']
            if url_filter:
                favorites = [x for x in favorites if x['url'] == url_filter]
            return {'favorites': favorites}
        else:
            # expected content: {"title": ..., "url": ...,  ...},
            # cf https://strasbourgeurometropole.github.io/slate/?shell#ajout-d-39-un-favori
            favorite = json.loads(request.body)
            # change type to typeId
            types_url = urlparse.urljoin(self.liferay_api_url, 'jsonws/favorite.favorite/get-types')
            try:
                resp = self.requests.get(types_url)
                resp.raise_for_status()
                types = resp.json()
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            # types: {"types": [{"id": "1", "name": "PLACE"}, ...]}
            types_dict = {x['name']: x['id'] for x in types['types']}
            favorite['typeId'] = types_dict.get(favorite.pop('type'))
            favorite['userId'] = name_id
            # send favorite
            url = urlparse.urljoin(self.liferay_api_url, 'jsonws/favorite.favorite/add-favorite')
            try:
                resp = self.requests.post(url, data=favorite)
                resp.raise_for_status()
                response = resp.json()
            except (ValueError, RequestException):
                return {'err': 2, 'err_desc': 'invalid service answer'}
            if response.get('success'):
                return {'err': 0, 'err_desc': response.get('success')}
            else:
                return {'err': 1, 'err_desc': response.get('error')}

    @endpoint(
        perm='can_access',
        name='favorites',
        methods=['post'],
        description=_('Delete favorite'),
        pattern=r'(?P<favorite_id>\w+)/delete$',
        example_pattern='{favorite_id}/delete',
        parameters={
            'favorite_id': {'description': _('Favorite Identifier'), 'example_value': '16'},
        },
    )
    def favorite_delete(self, request, name_id, favorite_id, **kwargs):
        url = urlparse.urljoin(self.liferay_api_url, 'jsonws/favorite.favorite/delete-favorite')
        params = {'userId': name_id, 'favoriteId': favorite_id}
        try:
            resp = self.requests.post(url, data=params)
            resp.raise_for_status()
            response = resp.json()
        except (ValueError, RequestException):
            return {'err': 2, 'err_desc': 'invalid service answer'}
        if response.get('success'):
            return {'err': 0, 'err_desc': response.get('success')}
        else:
            return {'err': 1, 'err_desc': response.get('error')}
