'''
Datasource for a ClicRDV instance.

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

import datetime
from urllib.parse import quote as urlquote

import requests
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 gettext_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(
        _('Server'), max_length=64, choices=CLICRDV_SERVERS, default='sandbox.clicrdv.com'
    )
    group_id = models.IntegerField(_('Group Id'), default=0)
    apikey = models.CharField(_('API Key'), max_length=64)
    username = models.CharField(_('Username'), max_length=64)
    password = models.CharField(_('Password'), max_length=64)
    websource = models.CharField(_('Web source'), max_length=64, blank=True, null=True)
    default_comment = models.CharField(_('Default comment'), 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

    def request(self, uri, method='get', **kwargs):
        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
        basic_auth = requests.auth.HTTPBasicAuth(self.username, self.password)
        response = self.requests.request(method, url, auth=basic_auth, **kwargs)
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as e:
            try:
                error = response.json()
            except ValueError:
                error = 'invalid json, got %s' % response.text
            return {
                'success': False,
                'error': '%s : %s' % (e, error),
            }
        return response.json()

    @endpoint(name='interventionsets')
    def get_interventionsets(self, request, **kwargs):
        response = self.request('interventionsets')
        if 'error' in response:
            return response
        records = response.get('records', [])
        records.sort(key=lambda x: x['sort'])
        ret = []
        for record in records:
            if record.get('publicname'):
                ret.append({'id': record['id'], 'text': record['publicname'], 'details': record})
        return {'data': ret}

    @endpoint(name='interventionsets', pattern=r'(?P<set>\d+)/')
    def get_interventions(self, request, set, **kwargs):
        ret = []
        response = self.request('interventions?interventionset_id=%s' % set)
        if 'error' in response:
            return response
        records = response.get('records', [])
        records.sort(key=lambda x: x['sort'])
        for record in records:
            if record.get('publicname'):
                ret.append({'id': record['id'], 'text': record['publicname'], 'details': record})
        return {'data': 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' % urlquote(date_start)
        if date_end:
            request_uri = request_uri + '&end=%s' % urlquote(date_end)
        response = self.request(request_uri)
        if 'error' in response:
            return []
        for timeslot in response.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(key=lambda x: x.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(key=lambda x: x.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(key=lambda x: x.get('id'))
        return times

    def cancel(self, appointment_id, **kwargs):
        response = self.request('appointments/%s' % appointment_id, method='delete')
        if 'error' in response:
            return 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 = {
            '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 list(fields.keys()) + list(extra.keys()) + list(data.keys()):
            if fieldname.startswith('clicrdv_fiche_'):
                appointment['appointment']['fiche'][fieldname[14:]] = get_data(fieldname) or ''
        response = self.request('appointments', 'post', json=appointment)
        if 'error' in response:
            return response
        appointment_id = response.get('records')[0].get('id')
        return {
            'success': True,
            'appointment_id': appointment_id,
        }
