# passerelle.contrib.agoraplus
# 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 time
import calendar
import datetime

def date_from_iso(s):
    if 'T' in s:
        utc = time.strptime(s, '%Y-%m-%dT%H:%M:%SZ')
        timestamp = calendar.timegm(utc)
        return datetime.date.fromtimestamp(timestamp).strftime('%Y-%m-%d')
    else:
        return datetime.datetime.strptime(s, '%Y-%m-%d').date()

def booleanize(value):
    '''
    return True/False from a Formdata value
    '''
    if isinstance(value, dict):
        value = value['id']
    if isinstance(value, bool):
        return value
    value = ('%s' % value).lower()
    return not value.startswith('n')


class Formdata(object):
    def __init__(self, data):
        self.data = data

    def get_workflow_data(self, key):
        return self.data.get('workflow', {}).get('data', {}).get(key) or ''

    def get_extra(self, key):
        return self.data.get('extra', {}).get(key) or ''

    def get_field(self, key):
        return self.data.get('fields', {}).get(key) or ''

    def get_extra_or_field(self, key):
        return self.get_extra(key) or self.get_field(key)

    def get(self, key):
        value = self.get_extra_or_field(key)
        raw = self.get_extra_or_field(key + '_raw')
        if raw:
            return {'id': raw, 'text': value}
        return value

    def get_object(self, prefix):
        res = {}
        fields = self.data.get('fields', {})
        extra = self.data.get('extra', {})
        keys = [key for key in set(fields) | set(extra)
                if key.startswith(prefix + '_') and not key.endswith('_raw')]
        cut = len(prefix)+1
        for key in keys:
            res[key[cut:]] = self.get(key)
        return res

    def get_sas_id_from_workflow(self, kind):
        '''
        Guess if formdata had been stored in the sas previously.
        '''
        if 'sas_response' in self.data['workflow'].get('data', {}):
            if self.data['workflow']['data']['sas_response'].get('data', {}).get(kind):
                sas_id = self.data['workflow']['data']['sas_response']['data'][kind].get('id')
                if sas_id and sas_id.startswith('sas_'):
                    return sas_id

    def get_address(self, prefix='address'):
        address = self.get_object(prefix) or {}
        if 'city_structured' in address and 'post_code' in address['city_structured']:
            address['post_code'] = address['city_structured']['post_code']
            del address['city_structured']
        for ref in ('btq', 'type_of_street', 'street', 'city'):
            if ref not in address or not isinstance(address[ref], dict):
                address[ref] = {'id': '', 'text': address.get(ref, '')}

        # alternative address (out of references)
        for key in ('number', 'post_code', 'complements'):
            address[key] = address.get(key) or address.get(key + '_alt') or ''
            if (key + '_alt') in address:
                del address[key + '_alt']
        for key in ('street', 'city'):
            if not address.get(key, {}).get('text'):
                address[key] = {
                    'id':'',
                    'text': address.get(key + '_alt') or ''
                }
            if (key + '_alt') in address:
                del address[key + '_alt']

        address['text'] = u'{number}{btq[text]} {type_of_street[text]} {street[text]}'.format(**address).strip()
        if address['text']:
            address['full'] = u'{text}, {post_code} {city[text]}'.format(**address).strip()
        else:
            address['full'] = u'{post_code} {city[text]}'.format(**address).strip()
        return address

    def get_person(self, prefix='adult'):
        person = self.get_object(prefix)
        if not person or not person.get('last_name'):
            return None
        person['text'] = u'%(first_name)s %(last_name)s' % person
        return person

    def get_child(self, prefix='child'):
        child = self.get_object(prefix)
        if not child or not child.get('last_name'):
            return None
        child['text'] = u'%(first_name)s %(last_name)s' % child
        sex = child.get('sex', {}).get('id')
        if sex:
            child['sex'] = sex[0]
        else:
            child['sex'] = None
        for key in child:
            if key.endswith('_authorization'):
                child[key] = booleanize(child[key])
        if child.get('parental_autorithy_structured'):
            # parental_autorithy_structured => adults already exist, use IDs
            child['parental_autorithy'] = [{'id': adult['id']} for adult in
                                            child['parental_autorithy_structured']]
        elif 'id' in child.get('parental_autorithy', {}):
            # parental_autorithy had a "raw" value, stored in ['id']
            child['parental_autorithy'] = [{'id': pid} for pid in
                                            child['parental_autorithy']['id']]
        else:
            child['parental_autorithy'] = []
        # if child was already stored in sas, get its sas_id from workflow.data
        child['sas_id']  = self.get_sas_id_from_workflow('child')
        return child

    def get_contact(self, prefix='contact'):
        contact = self.get_object(prefix)
        if not contact or not contact.get('last_name'):
            return None
        contact['text'] = u'%(first_name)s %(last_name)s' % contact
        contact['address'] = self.get_address('contact_address')
        if 'children_structured' in contact:
            contact['children'] = [{
                'id': child['id'],
                'link': contact.get('link') or {'id':'', 'text': ''},
                'text': child['text'],
                'banned': contact.get('banned') or False,
                'parental_authority': contact.get('parental_authority') or False,
            } for child in contact['children_structured']]
            del contact['children_structured']
        else:
            contact['children'] = []
        for cleankey in ('link', 'banned', 'parental_authority'):
            if cleankey in contact:
                del contact[cleankey]
        # if contact was already stored in sas, get its sas_id from workflow.data
        contact['sas_id'] = self.get_sas_id_from_workflow('contact')
        return contact

    def get_informations(self):
        informations = self.get_object('informations')
        informations['number_of_children'] = int(informations.get('number_of_children') or '0')
        return informations

    def get_family(self):
        family = {}
        family['address'] = self.get_address()
        adult1 = self.get_person('adult1')
        adult1['index'] = 1
        adult1['address'] = family['address']
        adult1['employer_address'] = self.get_address('employer_address_adult1')
        adult2 = self.get_person('adult2') or {}
        if adult2:
            adult2['index'] = 2
            adult2['address'] = family['address']
            adult2['employer_address'] = self.get_address('employer_address_adult2')
        family['informations'] = self.get_informations()
        # if objects were already stored in sas, get sas_id from workflow.data
        family['sas_id'] = self.get_sas_id_from_workflow('family')
        adult1['sas_id'] = self.get_sas_id_from_workflow('adult1')
        adult2['sas_id'] = self.get_sas_id_from_workflow('adult2')
        return family, adult1, adult2

    def get_incomes_declaration(self):
        return {
            'date': self.data['receipt_time'][:10],
            'nb_part': self.get_extra_or_field('nb_part'),
            'allowance_adult_id': self.get_extra_or_field('allowance_adult_raw'),
            'allowance_number': self.get_extra_or_field('allowance_number'),
            'allowance_regime_id': self.get_extra_or_field('allowance_regime_raw'),
            'date_start': date_from_iso(self.get_workflow_data('precision_var_date_start_raw')),
            'date_stop': date_from_iso(self.get_workflow_data('precision_var_date_stop_raw')),
            'incomes_adult1': self.get_extra_or_field('incomes_adult1'),
            'incomes_adult2': self.get_extra_or_field('incomes_adult2'),
            'incomes_allowance': self.get_extra_or_field('incomes_allowance'),
            'rental': self.get_extra_or_field('rental'),
            'reimbursement': self.get_extra_or_field('reimbursement'),
            'declaration_type_id': self.get_extra_or_field('declaration_type_id'),
        }

    def get_comments(self):
        comments = self.get_field('comments') or ''
        for key in [key for key in self.data['fields'] if key.startswith('comments_')]:
            title = key[9:].upper().replace('_', ' ')
            comments += '[%s]\n%s\n\n' % (title, self.get_field(key))
        return comments

    def get_school_enrollment(self):
        comments = self.get_comments()
        return {
            'child_id': self.get_field('child_structured')['id'],
            'year_id': self.get_extra_or_field('year_raw'),
            'school_id': self.get_extra_or_field('school_raw'),
            'educational_stage_id': self.get_extra_or_field('educational_stage_raw'),
            'comments': comments,
            'date_start': self.get_extra_or_field('date_start'),
            'status_id': self.get_extra_or_field('status_raw'),
        }

    def get_nursery_enrollment(self):
        comments = self.get_comments()
        structures_ids = [] # ordered list of structures id (by preference)
        n = 1
        while self.get_field('structure_%s_raw' % n):
            structures_ids.append(self.get_field('structure_%s_raw' % n))
            n += 1
        return {
            'child_id': self.get_field('child_structured')['id'],
            'expected_birthdate': self.get_extra_or_field('expected_birthdate'),
            'year_id': self.get_extra_or_field('year_raw'),
            'date': self.data['receipt_time'][:10],
            'date_start': self.get_extra_or_field('date_start'),
            'date_stop': self.get_extra_or_field('date_stop'),
            'type_structure_id': self.get_extra_or_field('type_structure_raw'),
            'structures_ids': structures_ids,
            'admission_mode_id': self.get_extra_or_field('admission_mode_raw'),
            'comments': comments,
        }
