# Passerelle - uniform access to data and services
# Copyright (C) 2015  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 requests
import logging

from django.db import models
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _

from jsonfield import JSONField

from passerelle.base.models import BaseResource


class SubscriptionsSetError(Exception):
    http_status = 503


class SubscriptionsGetError(Exception):
    http_status = 503


class MeyzieuNewsletters(BaseResource):
    url = models.URLField(verbose_name=_('Newsletter service URL'))
    apikey = models.CharField(max_length=32, verbose_name=_('API key'))
    transport_titles_mapping = JSONField(_('Mapping of transports ids and titles'))

    category = _('Newsletter registration')

    class Meta:
        verbose_name = _('Meyzieu Newsletters')

    @classmethod
    def is_enabled(cls):
        return True

    @classmethod
    def get_icon_class(cls):
        return 'ressources'

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

    def get_transport_title(self, transport_id):
        return self.transport_titles_mapping.get(transport_id, transport_id)

    def get_newsletters(self):
        logger = logging.getLogger(__name__)
        try:
            params = {
                'method': 'meyzieu.connection.getListRubrique',
                'apikey': self.apikey
            }
            response = requests.get(self.url, params=params)
        except (requests.HTTPError, requests.ConnectionError), e:
            logger.error('<%r> getListRubrique failed: %r', self.slug, e)
            return []
        newsletters = {}
        for kind, value in response.json().iteritems():
            for item in value:
                newsletter = newsletters.setdefault(item['id_rubrique'], {})
                newsletter['id'] = item['id_rubrique']
                newsletter['text'] = item['titre']
                kinds = newsletter.setdefault('transports', [])
                kinds.append({'id': kind, 'text': self.get_transport_title(kind)})
        return newsletters.values()

    def set_subscriptions(self, email, subscriptions):
        logger = logging.getLogger(__name__)
        currents = {}
        subs = {}
        remove = {}
        add = {}
        current_subscriptions = self.get_subscriptions(email)

        for current in current_subscriptions:
            currents[current['id']] = current

        for sub in subscriptions:
            subs[sub['id']] = sub

        base = {
            'method': 'meyzieu.connection.addAbonnement',
            'apikey': self.apikey,
            'auteur[email]': email,
        }

        for subscription_id, data in currents.iteritems():
            if subscription_id not in subs:
                continue
            transports = [i['id'] for i in data['transports']]
            new = subs.get(subscription_id, [])
            remove[subscription_id] = set(transports) - set(new['transports'])

        for subscription_id, kinds in subs.iteritems():
            current_transports = currents.get(subscription_id, {}).get('transports', [])
            transports = [i['id'] for i in current_transports]
            new_transports = kinds['transports']
            if new_transports:
                new = set(new_transports) - set(transports)
                add[subscription_id] = new

        for i, (subscription_id, kinds) in enumerate(remove.iteritems()):
            params = base.copy()
            params.update({
                'desabonnement': 1,
                'id_abonnement[%d]' % i: subscription_id
            })
            for k in kinds:
                params.update({'type': k})
                try:
                    logger.info("<%r> '%r' unsubscribed from %r[%r]", self.slug,
                                email, subscription_id, k)
                    response = requests.get(self.url, params=params)
                except (requests.HTTPError, requests.ConnectionError), e:
                    logger.error("<%r> '%r' addAbonnement failed: %r", self.slug, email, e)
                    raise SubscriptionsGetError('error on getting subscriptions')

        for i, (subscription_id, kinds) in enumerate(add.iteritems()):
            params = base.copy()
            params.update({
                'id_abonnement[%d]' % i: subscription_id,
                'desabonnement': 0,
            })
            for k in kinds:
                params['type'] = k
                try:
                    logger.info("<%r> '%r' subscribed to %r[%r]", self.slug,
                                email, subscription_id, k)
                    response = requests.get(self.url, params=params)
                except (requests.HTTPError, requests.ConnectionError), e:
                    logger.error("<%r> '%r' addAbonnement failed: %r", self.slug, email, e)
                    raise SubscriptionsSetError('error on setting subscriptions')
        return True

    def get_subscriptions(self, email):
        logger = logging.getLogger(__name__)
        try:
            params = {
                'method': 'meyzieu.connection.getListAbonnement',
                'apikey': self.apikey,
                'auteur[email]': email,
            }
            response = requests.get(self.url, params=params)
        except (requests.HTTPError, requests.ConnectionError), e:
            logger.error("<%r> '%r' getListAbonnement failed: %r", self.slug, email, e)
            raise SubscriptionsGetError('error on getting subscriptions')

        n = dict((i.pop('id'), i) for i in self.get_newsletters())
        if 'abonnement' in response.json():
            newsletters = {}
            available_kinds = []
            for kind, value in response.json()['abonnement'].iteritems():
                available_kinds.append(kind)
                for item in value:
                    if item['id_abonnement'] not in n:
                        continue
                    newsletter = newsletters.setdefault(item['id_abonnement'], {})
                    newsletter['id'] = item['id_abonnement']
                    newsletter['text'] = n[item['id_abonnement']].get('text')
                    transports = newsletter.setdefault('transports', [])
                    transports.append({'id': kind, 'text': self.get_transport_title(kind)})
            return newsletters.values()
        else:
            return []
