'''
Datasource for a ClicRDV instance.

It is a gateway to http://developers.clicrdv.com/fr/rest-api.html
'''

import base64
import datetime
import json
import urllib2

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.dateformat import format as date_format
from django.utils.dateformat import time_format
from django.utils.translation import ugettext_lazy as _

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

CLICRDV_SERVERS = (
    ('www.clicrdv.com', 'Production (www.clicrdv.com)'),
    ('sandbox.clicrdv.com', 'SandBox (sandbox.clicrdv.com)')
)

class ClicRdv(BaseResource):
    server = models.CharField(max_length=64,
        choices=CLICRDV_SERVERS,
        default='sandbox.clicrdv.com')
    group_id = models.IntegerField(default=0)
    apikey = models.CharField(max_length=64)
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)
    websource = models.CharField(max_length=64, blank=True, null=True)
    default_comment = models.CharField(max_length=250, blank=True, null=True)

    parameters = ('method', 'set', 'intervention', 'date', 'websource', 'id')

    category = _('Business Process Connectors')

    class Meta:
        verbose_name = _('Clicrdv Agenda')

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

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

    def get_request(self, uri):
        url = 'https://%s/api/v1/groups/%s/%s' % (self.server, self.group_id, uri)
        if '?' in url:
           url = url + '&apikey=%s&format=json' % self.apikey
        else:
           url = url + '?apikey=%s&format=json' % self.apikey
        req = urllib2.Request(url)
        authheader = 'Basic ' + \
            base64.encodestring('%s:%s' % (self.username, self.password))[:-1]
        req.add_header('Authorization', authheader)
        return req

    def get_json(self, uri):
        req = self.get_request(uri)
        return json.load(urllib2.urlopen(req))

    @endpoint(name='interventionsets', serializer_type='json-api')
    def get_interventionsets(self, request, **kwargs):
        records = self.get_json('interventionsets').get('records')
        records.sort(lambda x,y: cmp(x['sort'], y['sort']))
        ret = []
        for record in records:
            if record.get('publicname'):
                ret.append({'id': record['id'], 'text': record['publicname'], 'details': record})
        return ret

    @endpoint(name='interventionsets', pattern='(?P<set>\d+)/', serializer_type='json-api')
    def get_interventions(self, request, set, **kwargs):
        ret = []
        records = self.get_json('interventions?interventionset_id=%s' % set).get('records')
        records.sort(lambda x,y: cmp(x['sort'], y['sort']))
        for record in records:
            if record.get('publicname'):
                ret.append({'id': record['id'], 'text': record['publicname'], 'details': record})
        return ret

    def get_available_timeslots(self, intervention, date_start=None, date_end=None):
        timeslots = []
        iid = int(intervention)
        request_uri = 'availabletimeslots?intervention_ids[]=%s' % iid
        if date_start is None:
            date_start = datetime.datetime.today().strftime('%Y-%m-%d')
        if date_end is None:
            date_end = (datetime.datetime.today() + datetime.timedelta(366)).strftime('%Y-%m-%d')
        if date_start:
            request_uri = request_uri + '&start=%s' % urllib2.quote(date_start)
        if date_end:
            request_uri = request_uri + '&end=%s' % urllib2.quote(date_end)
        for timeslot in self.get_json(request_uri).get('availabletimeslots'):
            timeslots.append(timeslot.get('start'))
        timeslots.sort()
        return timeslots

    def get_datetimes(self, intervention, **kwargs):
        datetimes = []
        for timeslot in self.get_available_timeslots(intervention):
            parsed = datetime.datetime.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
            datetimed = {'id': parsed.strftime('%Y-%m-%d-%H:%M:%S'),
                        'text': date_format(parsed, 'j F Y H:i')}
            datetimes.append(datetimed)
        datetimes.sort(lambda x,y: cmp(x.get('id'), y.get('id')))
        return datetimes

    def get_dates(self, intervention, **kwargs):
        dates = []
        for timeslot in self.get_available_timeslots(intervention):
            parsed = datetime.datetime.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
            date = {'id': parsed.strftime('%Y-%m-%d'),
                    'text': date_format(parsed, 'j F Y')}
            if date in dates:
                continue
            dates.append(date)
        dates.sort(lambda x,y: cmp(x.get('id'), y.get('id')))
        return dates

    def get_times(self, intervention, date, **kwargs):
        if not date:
            raise Exception('no date value')
        times = []
        for timeslot in self.get_available_timeslots(intervention,
                                date_start='%s 00:00:00' % date,
                                date_end='%s 23:59:59' % date):
            parsed = datetime.datetime.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
            timed = {'id': parsed.strftime('%H:%M:%S'),
                    'text': time_format(parsed, 'H:i')}
            times.append(timed)
        times.sort(lambda x,y: cmp(x.get('id'), y.get('id')))
        return times

    def cancel(self, id, **kwargs):
        appointment_id = int(id)
        req = self.get_request('appointments/%s' % appointment_id)
        req.get_method = (lambda: 'DELETE')
        try:
            fd = urllib2.urlopen(req)
            none = fd.read()
        except urllib2.HTTPError as e:
            # clicrdv will return a "Bad Request" (HTTP 400) response
            # when it's not possible to remove an appointment
            # (for example because it's too late)
            response = e.read()
            response = json.loads(response)
            return {'success': False, 'error': response}
        return {'success': True}


    def create_appointment(self, intervention, websource, data):
        fields = data.get('fields') or {}
        extra = data.get('extra') or {}
        def get_data(key, default=None):
            return data.get(key) or extra.get(key) or fields.get(key) or default
        if intervention:
            intervention = int(intervention)
        else:
            intervention = int(get_data('clicrdv_intervention_raw'))
        date = get_data('clicrdv_date_raw')
        time = get_data('clicrdv_time_raw')
        if not date and not time:
            date = get_data('clicrdv_datetime_raw')
        else:
            date = date + ' ' + time
        if websource is None:
            websource = self.websource or ''
        appointment = {
            'fiche': {
                'firstname': get_data('clicrdv_firstname') or '-',
                'lastname': get_data('clicrdv_lastname') or '-',
                'email': get_data('clicrdv_email', ''),
                'firstphone': get_data('clicrdv_firstphone', ''),
                'secondphone': get_data('clicrdv_secondphone', ''),
             },
             'date': date,
             'intervention_ids': [intervention],
             'websource': websource,
        }
        comments = get_data('clicrdv_comments') or self.default_comment
        if comments:
            appointment['comments'] = comments
        # optional parameters, if any...
        for fieldname in (fields.keys() + extra.keys() + data.keys()):
            if fieldname.startswith('clicrdv_fiche_'):
                appointment['fiche'][fieldname[14:]] = get_data(fieldname) or ''
        req = self.get_request('appointments')
        req.add_data(json.dumps({'appointment': appointment}))
        req.add_header('Content-Type', 'application/json')
        try:
            fd = urllib2.urlopen(req)
        except urllib2.HTTPError, e:
            try:
                error = json.load(e.fp)[0].get('error')
            except:
                error = 'Unknown error (Passerelle)'
            return {
                'success': False,
                'error': error,
            }
        else:
            success = True
            response = json.load(fd)
            appointment_id = response.get('records')[0].get('id')
            return {
                'success': True,
                'appointment_id': appointment_id,
            }
