import base64
import json

import pytest
import responses
from django.contrib.contenttypes.models import ContentType

import tests.utils
from passerelle.apps.esabora.models import Esabora
from passerelle.base.models import AccessRight, ApiUser

ESABORA_DO_SEARCH_RESPONSE = {
    'searchId': '23568',
    'nbResults': 2,
    'columnList': [
        'Column 1',
        'Column 2',
        'Column 3',
    ],
    'keyList': ['internal.id'],
    'rowList': [
        {
            'columnDataList': [
                'Foo 1',
                'Foo 2',
                'Foo 3',
            ],
            'keyDataList': ['id1'],
        },
        {
            'columnDataList': [
                'Bar 1',
                'Bar 2',
                'Bar 3',
            ],
            'keyDataList': ['id2'],
        },
    ],
}
ESABORA_DO_TREATMENT_RESPONSE = {
    'action': 'insert',
    'keyList': ['internal.id'],
    'keyDataList': ['14'],
}


@pytest.fixture()
def connector(db):
    api = ApiUser.objects.create(username='all', keytype='', key='')
    connector = Esabora.objects.create(
        service_url='http://example.esabora/ws/rest/', api_key='1234', slug='test'
    )
    obj_type = ContentType.objects.get_for_model(connector)
    AccessRight.objects.create(
        codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=connector.pk
    )
    return connector


@responses.activate
def test_authentication(app, connector):
    url = tests.utils.generic_endpoint_url('esabora', 'do-search')
    responses.add(
        responses.POST,
        f'{connector.service_url}mult/',
        json=ESABORA_DO_SEARCH_RESPONSE,
        status=200,
    )

    app.post_json(url, params={'search_name': 'foo', 'criterions/bar': 'noop'})
    assert len(responses.calls) == 1
    assert responses.calls[0].request.headers['authorization'] == f'Bearer {connector.api_key}'


@responses.activate
def test_do_treatment(app, connector):
    url = tests.utils.generic_endpoint_url('esabora', 'do-treatment')
    responses.add(
        responses.POST,
        f'{connector.service_url}modbdd/',
        json=ESABORA_DO_TREATMENT_RESPONSE,
        status=200,
    )

    file_content1 = b'this is a test file'
    file_content2 = b'this is another test file'
    file_content3 = b'this is yet another test file'

    payload = {
        'treatment_name': 'Import HISTOLOGE',
        'Adresse_Latitude': 12.3,
        'Adresse_Ville': 'Marseille',
        # documents that will be unflattened
        'PJ_Documents/0': {
            'filename': 'test1.pdf',
            'content_type': 'application/pdf',
            'content': base64.b64encode(file_content1).decode(),
        },
        'PJ_Documents/1': {
            'filename': 'test2.pdf',
            'content_type': 'application/pdf',
            'content': base64.b64encode(file_content2).decode(),
        },
        # empty field, will be skipped
        'PJ_Documents/2': "",
        # ensure we handle single documents as well
        'PJ_Documents_Autre': {
            'filename': 'test3.pdf',
            'content_type': 'application/pdf',
            'content': base64.b64encode(file_content3).decode(),
        },
    }

    expected_payload = {
        'treatmentName': 'Import HISTOLOGE',
        'fieldList': [
            {'fieldName': 'Adresse_Latitude', 'fieldValue': 12.3},
            {'fieldName': 'Adresse_Ville', 'fieldValue': 'Marseille'},
            {
                'fieldName': 'PJ_Documents',
                'fieldDocumentUpdate': 1,
                'fieldValue': [
                    {
                        'documentName': 'test1.pdf',
                        'documentContent': base64.b64encode(file_content1).decode(),
                        # len(file_content1)
                        'documentSize': 19,
                    },
                    {
                        'documentName': 'test2.pdf',
                        'documentContent': base64.b64encode(file_content2).decode(),
                        'documentSize': 25,
                    },
                ],
            },
            {
                'fieldName': 'PJ_Documents_Autre',
                'fieldDocumentUpdate': 1,
                'fieldValue': [
                    {
                        'documentName': 'test3.pdf',
                        'documentContent': base64.b64encode(file_content3).decode(),
                        'documentSize': 29,
                    }
                ],
            },
        ],
    }

    response = app.post_json(url, params=payload)
    assert response.json == {'err': 0, 'action': 'insert', 'id': '14', 'internalid': '14', 'text': '14'}

    assert len(responses.calls) == 1
    assert responses.calls[0].request.params['task'] == 'doTreatment'
    response_data = json.loads(responses.calls[0].request.body)
    assert response_data == expected_payload


@responses.activate
def test_do_search(app, connector):
    url = tests.utils.generic_endpoint_url('esabora', 'do-search')
    responses.add(
        responses.POST,
        f'{connector.service_url}mult/',
        json=ESABORA_DO_SEARCH_RESPONSE,
        status=200,
    )

    payload = {'search_name': 'WS_ETAT_SAS', 'criterions/foo': 'bar'}

    expected_payload = {
        'searchName': 'WS_ETAT_SAS',
        'criterionList': [{'criterionName': 'foo', 'criterionValueList': ['bar']}],
    }

    response = app.post_json(url, params=payload)

    expected = {
        'err': 0,
        'data': [
            {
                'id': 'id1',
                'text': 'Foo 1',
                'internalid': 'id1',
                "column_1": 'Foo 1',
                "column_2": 'Foo 2',
                "column_3": 'Foo 3',
            },
            {
                'id': 'id2',
                'text': 'Bar 1',
                'internalid': 'id2',
                "column_1": 'Bar 1',
                "column_2": 'Bar 2',
                "column_3": 'Bar 3',
            },
        ],
        'meta': {
            'searchId': '23568',
            'nbResults': 2,
            'columns_name': {
                "column_1": 'Column 1',
                "column_2": 'Column 2',
                "column_3": 'Column 3',
            },
            'keys_name': {
                "internalid": 'internal.id',
            },
        },
    }
    assert response.json == expected
    assert responses.calls[0].request.params['task'] == 'doSearch'
    assert json.loads(responses.calls[0].request.body) == expected_payload


@responses.activate
def test_do_treatment_arbitrary_endpoint(app, connector):
    url = tests.utils.generic_endpoint_url('esabora', 'do-treatment')
    responses.add(
        responses.POST,
        f'{connector.service_url}addevt/',
        json=ESABORA_DO_TREATMENT_RESPONSE,
        status=200,
    )

    payload = {
        'endpoint': 'addevt',
        'treatment_name': 'Import Event',
        'Adresse_Latitude': 12.3,
        'Adresse_Ville': 'Marseille',
    }

    expected_payload = {
        'treatmentName': 'Import Event',
        'fieldList': [
            {'fieldName': 'Adresse_Latitude', 'fieldValue': 12.3},
            {'fieldName': 'Adresse_Ville', 'fieldValue': 'Marseille'},
        ],
    }

    response = app.post_json(url, params=payload)
    assert response.json == {'err': 0, 'action': 'insert', 'id': '14', 'internalid': '14', 'text': '14'}

    assert len(responses.calls) == 1
    assert responses.calls[0].request.params['task'] == 'doTreatment'
    response_data = json.loads(responses.calls[0].request.body)
    assert response_data == expected_payload


@responses.activate
def test_post_raises_proper_error(app, connector):
    url = tests.utils.generic_endpoint_url('esabora', 'do-treatment')
    responses.add(
        responses.POST,
        f'{connector.service_url}addevt/',
        json={'foo': 'bar'},
        status=400,
    )

    payload = {
        'endpoint': 'addevt',
        'treatment_name': 'Import Event',
        'Adresse_Latitude': 12.3,
        'Adresse_Ville': 'Marseille',
    }

    expected_response = {
        'err': 1,
        'err_class': 'passerelle.utils.jsonresponse.APIError',
        'data': {'content': {'foo': 'bar'}, 'status_code': 400},
        'err_desc': 'Esabora platform "http://example.esabora/ws/rest/" answered with HTTP error',
    }

    response = app.post_json(url, params=payload)
    assert response.json == expected_response
