# -*- coding: utf-8 -*-

import pytest
import os
from uuid import uuid4
import zipfile
import logging

from django.core.exceptions import ValidationError
from django.core.files import File
from django.core.wsgi import get_wsgi_application
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.http import urlencode
from django.core.files import File
from django.core.management import call_command
from django.core.management.base import CommandError

from family.models import GenericFamily, Family, FamilyLink
from family.models import Invoice, Adult, Child, DATETIME_FORMAT

from django.contrib.contenttypes.models import ContentType

from passerelle.base.models import ApiUser, AccessRight


pytestmark = pytest.mark.django_db

nameid = 'foobarnameid'
API_KEY = 'family'

@pytest.fixture
def setup():
    from webtest import TestApp
    filepath = os.path.join(os.path.dirname(__file__), 'data', 'family_data.zip')
    resource = GenericFamily.objects.create(title='test', slug='test', archive=filepath)
    api = ApiUser.objects.create(username='family', keytype='API', key=API_KEY)
    obj_type = ContentType.objects.get_for_model(GenericFamily)
    AccessRight.objects.create(codename='can_access', apiuser=api,
                    resource_type=obj_type, resource_pk=resource.pk)
    app = TestApp(get_wsgi_application())
    return app, resource

def test_link_to_family(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '9407',
                        'password': 'gkh0UrrH', 'apikey': API_KEY})
    assert r.json['data']
    assert FamilyLink.objects.filter(resource=resource, name_id=nameid).exists()

def test_unlink_from_family(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
            'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '1364',
                        'password': 'Li6LN1ID', 'apikey': API_KEY})
    assert r.json['data']
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
            'slug': resource.slug, 'endpoint': 'family', 'rest': 'unlink/'}),
                params={'NameID': nameid, 'apikey': API_KEY})
    assert r.json['data']
    assert not FamilyLink.objects.filter(resource=resource, name_id=nameid).exists()

def test_family_infos(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
            'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '9407',
                        'password': 'gkh0UrrH', 'apikey': API_KEY})
    assert r.json['data']
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family/'}),
                params={'NameID': nameid, 'apikey': API_KEY})
    data = r.json['data']
    assert data['id']
    assert data['adults']
    assert data['children']


def test_family_members(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '23762',
                        'password': 's6HliUMX', 'apikey': API_KEY})
    assert r.json['data']
    for typ in ('adults', 'children'):
        r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                    'slug': resource.slug, 'endpoint': 'family', 'rest': '%s/' % typ}),
                    params={'NameID': nameid, 'apikey': API_KEY})
        data = r.json['data']

        assert len(data)
        for person in data:
            assert person['id']
            assert person['first_name']
            assert person['last_name']

def test_get_family_invoices(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '19184',
                        'password': '8xUhrK6e', 'apikey': API_KEY})
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'regie', 'rest': 'invoices/'}),
                params={'NameID': nameid, 'apikey': API_KEY})
    assert r.json['data']
    for i in r.json['data']:
        assert not i['paid']

    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'regie',
                'rest': 'invoices/history/'}),
                params={'NameID': nameid, 'apikey': API_KEY})
    assert r.json['data']
    for i in r.json['data']:
        assert i['paid']
        assert i['has_pdf']

def test_get_family_invoice(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '19184',
                        'password': '8xUhrK6e', 'apikey': API_KEY})
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                        'slug': resource.slug, 'endpoint': 'regie',
                        'rest': 'invoices/'}),
                        params={'NameID': nameid, 'apikey': API_KEY})
    assert r.json['data']
    invoice = r.json['data'][0]

    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'regie',
                'rest': 'invoice/%s/' % invoice['label']}),
                params={'apikey': API_KEY})
    assert r.json['data']
    for field in ('label', 'amount', 'paid', 'created', 'pay_limit_date'):
        assert r.json['data'][field] == invoice[field]

def test_get_invoice_pdf(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '11959',
                        'password': '1WI6JOux', 'apikey': API_KEY})
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                        'slug': resource.slug, 'endpoint': 'regie',
                        'rest': 'invoices/history/'}),
                params={'NameID': nameid, 'apikey': API_KEY})
    invoice = r.json['data'][0]
    assert invoice
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                        'slug': resource.slug, 'endpoint': 'regie',
                        'rest': 'invoice/%s/pdf/' % invoice['label']}),
                params={'apikey': API_KEY})
    assert 'Content-Type' in r.headers
    assert 'Content-Disposition' in r.headers
    assert r.headers['Content-Type'] == 'application/pdf'
    assert r.headers['Content-Disposition'] == 'attachment; filename=%s.pdf' % invoice['label']

def test_pay_invoice(setup):
    app, resource = setup
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                'slug': resource.slug, 'endpoint': 'family', 'rest': 'link/'}),
                params={'NameID': nameid, 'login': '19184',
                        'password': '8xUhrK6e', 'apikey': API_KEY})
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                        'slug': resource.slug, 'endpoint': 'regie',
                        'rest': 'invoices/'}),
                params={'NameID': nameid})
    invoice = r.json['data'][0]
    payment_url = reverse('generic-endpoint', kwargs={'connector': 'family',
                        'slug': resource.slug, 'endpoint': 'regie',
                        'rest': 'invoice/%s/pay/' % invoice['label']})
    r = app.post_json(payment_url + '?' + urlencode({'apikey': API_KEY}),
                    {'transaction_id': str(uuid4()),
                     'transaction_date': timezone.now().strftime(DATETIME_FORMAT)}
    )
    assert r.json['data']
    r = app.get(reverse('generic-endpoint', kwargs={'connector': 'family',
                        'slug': resource.slug, 'endpoint': 'regie',
                        'rest': 'invoice/%s/' % invoice['label']}),
                params={'apikey': API_KEY})
    assert r.json['data']['paid']
    assert float(r.json['data']['amount']) == 0

def test_fondettes_concerto_loader():
    Invoice.objects.all().delete()
    filepath = os.path.join(os.path.dirname(__file__), 'data', 'family_data_fondettes.zip')
    resource = GenericFamily.objects.create(title='test fondettes',
            slug='test-fondettes', archive=filepath, file_format='concerto_fondettes')
    assert Invoice.objects.filter(resource=resource).count() == 630
    assert len([x for x in Invoice.objects.filter(resource=resource) if x.has_pdf]) == 4
    assert Invoice.objects.filter(paid=True).count() == 314
    assert Invoice.objects.filter(paid=False).count() == 316

def test_archive_validation():
    filepath = os.path.join(os.path.dirname(__file__), 'data', 'iparapheur.wsdl')
    resource = GenericFamily.objects.create(title='test', slug='test', archive=filepath)
    resource.archive = File(open(filepath))
    with pytest.raises(ValidationError):
        resource.clean()

def test_fondettes_concerto_validation():
    filepath = os.path.join(os.path.dirname(__file__), 'data', 'family_data_fondettes.zip')
    resource = GenericFamily.objects.create(title='test fondettes',
            slug='test fondettes', archive=filepath, file_format='concerto_fondettes')
    resource.archive = File(open(filepath))
    resource.clean()

    filepath = os.path.join(os.path.dirname(__file__), 'data', 'family_data.zip')
    resource.archive = File(open(filepath))
    with pytest.raises(ValidationError):
        resource.clean()

def test_orleans_concerto_loader():
    # all related objects will also be deleted
    Family.objects.all().delete()
    filepath = os.path.join(os.path.dirname(__file__), 'data', 'family_data_orleans.zip')
    resource = GenericFamily(title='test orleans',
            slug='test-orleans', archive=filepath, file_format='concerto_orleans')
    from family.loaders.concerto_orleans import Loader
    loader = Loader(resource)
    loader.archive = zipfile.ZipFile(filepath)

    families = loader.build_families()
    assert len(families) == 18
    for family in families.values():
        assert family['external_id']
        assert family['adults']
        assert family['login']
        assert family['password']
        assert family['zipcode']
        assert family['city']
        assert len(family['adults']) > 0

        for adult in family['adults']:
            assert adult['first_name']
            assert adult['last_name']
            assert adult['sex']
            assert adult['external_id']
            assert adult['zipcode']
            assert adult['city']
        assert len(family['children']) >= 1
        for child in family['children']:
            assert child['external_id']
            assert child['first_name']
            assert child['last_name']
            assert child['sex']
            assert 'birthdate' in child

        assert 'invoices' in family
        if family['invoices']:
            for invoice in family['invoices']:
                assert invoice['external_id']
                assert invoice['label']
                assert invoice['issue_date']
                assert invoice['online_payment']
                assert 'autobilling' in invoice
                assert 'amount' in invoice
                assert 'total_amount' in invoice

    # there are 4 families with invoices in test data
    assert len([f for f in families.values() if f['invoices']]) == 4
    # and 14 families with no invoices
    assert len([f for f in families.values() if not f['invoices']]) == 14

    resource.save()

    assert Family.objects.filter(resource=resource).count() == 18
    assert Adult.objects.all().count() == 31
    assert Child.objects.all().count() == 35
    assert Invoice.objects.filter(resource=resource).count() == 26
    assert Invoice.objects.filter(resource=resource, total_amount=0).count() == 2
    assert Invoice.objects.filter(online_payment=True).count() == 26

def test_orleans_data_import_command():
    with pytest.raises(CommandError) as error:
        call_command('import_orleans_data')
    assert str(error.value) == 'No data dir specified.'

    with pytest.raises(CommandError) as error:
        call_command('import_orleans_data', data_dir='/tmp/orleans_data')
    assert str(error.value) == 'Directory /tmp/orleans_data does not exist.'

    cur_dir = os.path.dirname(__file__)
    data_dir = os.path.join(cur_dir, 'data')
    with pytest.raises(CommandError) as error:
        call_command('import_orleans_data', data_dir=data_dir)
    assert str(error.value) == 'File %s/exports_prcit.zip does not exist.' % data_dir

    resource = GenericFamily.objects.create(title='test orleans',
                archive=os.path.join(data_dir, 'family_data_orleans.zip'),
                slug='test-orleans', file_format='concerto_orleans')

    # cleanup data before launching import
    Family.objects.filter(resource=resource).delete()
    Invoice.objects.filter(resource=resource).delete()

    call_command('import_orleans_data', data_dir=data_dir,
                 archive_name='family_data_orleans.zip')
    assert Family.objects.filter(resource=resource).count() == 0
    assert Invoice.objects.filter(resource=resource).count() == 0

    with pytest.raises(Exception) as error:
        call_command('import_orleans_data', data_dir=data_dir,
                     archive_name='family_data_orleans.zip',
                     connector='test-orleans')
    assert "No such file or directory: '%s/factures'" % data_dir in str(error.value)

    # create temporary expected dir for invoices
    invoices_dir = os.path.join(data_dir, 'factures')
    os.mkdir(invoices_dir)
    call_command('import_orleans_data', data_dir=data_dir,
                 archive_name='family_data_orleans.zip',
                 connector='test-orleans')
    os.rmdir(invoices_dir)

    assert Family.objects.filter(resource=resource).count() > 0
    assert Adult.objects.all().count() > 0
    assert Child.objects.all().count() > 0
    assert Invoice.objects.filter(resource=resource).count() > 0

def test_incorrect_orleans_data(caplog):
    filepath = os.path.join(os.path.dirname(__file__), 'data',
                            'family_incorrect_data_orleans.zip')
    resource = GenericFamily.objects.create(title='test orleans',
                            slug='test-orleans', archive=filepath,
                            file_format='concerto_orleans')
    for record in caplog.records():
        assert 'Error occured while importing data:' in record.message
        assert record.name == 'passerelle.resource.genericfamily.test-orleans'
        assert record.levelno == logging.ERROR
