from unittest import mock
from urllib import parse as urlparse

import pytest
from django.contrib.contenttypes.models import ContentType
from requests.exceptions import HTTPError

from passerelle.apps.clicrdv.models import ClicRdv
from passerelle.base.models import AccessRight, ApiUser
from tests.test_manager import login


@pytest.fixture
def connector(db):
    return ClicRdv.objects.create(
        slug='test', group_id='5242', apikey='test', username='test', password='test'
    )


def test_connector_is_legacy(connector, app, admin_user):
    app = login(app)

    # check the existing instance is displayed on homepage
    resp = app.get('/manage/', status=200)
    assert 'Clicrdv Agenda' in resp

    # and check it's not available on "add connector" page
    resp = resp.click('Add Connector')
    assert 'Clicrdv Agenda' not in resp


@mock.patch('passerelle.utils.Request.request')
def test_request_call(mocked_request, app, connector):
    mocked_request.json.return_value = "foo"
    connector.request('bar')
    assert mocked_request.call_count == 1
    req = mocked_request.call_args[0][1]
    assert req.startswith('https://sandbox.clicrdv.com/api/v1/groups/5242/bar')


@mock.patch('passerelle.utils.Request.request')
def test_interventionsets(mocked_request, app, connector):
    response = mock.Mock()
    response.json.return_value = {
        "totalRecords": 2,
        "records": [
            {
                "sort": 1,
                "publicname": "Une Demande de Passeport",
                "name": "Demande",
                "id": 7032,
                "group_id": 5242,
            },
            {
                "sort": 2,
                "publicname": "Un Retrait de Passeport",
                "name": "Retrait",
                "id": 7033,
                "group_id": 5242,
            },
        ],
    }
    mocked_request.return_value = response
    resp = app.get('/clicrdv/test/interventionsets/')
    assert len(resp.json.get('data')) == 2
    assert resp.json.get('data')[0]['text'] == 'Une Demande de Passeport'


@mock.patch('passerelle.utils.Request.request')
def test_interventionsets_details(mocked_request, app, connector):
    response = mock.Mock()
    response.json.return_value = {
        "totalRecords": 2,
        "records": [
            {
                "sort": 1,
                "publicname": "pour une personne",
                "description": None,
                "name": "1 personne",
                "interventionset_id": 7032,
                "group_id": 5242,
                "id": 63258,
                "abbr": "1 demande",
            },
            {
                "sort": 2,
                "publicname": "pour deuxs personnes",
                "description": None,
                "name": "2 personnes",
                "interventionset_id": 7032,
                "group_id": 5242,
                "id": 63259,
                "abbr": "2 demandes",
            },
        ],
    }
    mocked_request.return_value = response
    resp = app.get('/clicrdv/test/interventionsets/7032/')
    assert len(resp.json.get('data')) == 2
    assert resp.json.get('data')[0]['text'] == 'pour une personne'


@mock.patch('passerelle.utils.Request.request')
def test_interventions_get_datetimes(mocked_request, app, connector):
    response = mock.Mock()
    response.json.return_value = {"availabletimeslots": []}
    mocked_request.return_value = response
    resp = app.get('/clicrdv/test/interventions/63258/dates/')
    assert resp.json.get('data') == []
    assert resp.json.get('err') == 0
    assert mocked_request.call_count == 1
    url = mocked_request.call_args[0][1]
    # https://sandbox.clicrdv.com/api/v1/groups/5242/availabletimeslots?
    #   intervention_ids[]=63258&start=2016-09-21&end=2017-09-22&apikey=test&format=json
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
    query = urlparse.parse_qs(query, keep_blank_values=True)
    assert scheme == 'https'
    assert netloc == 'sandbox.clicrdv.com'
    assert path == '/api/v1/groups/5242/availabletimeslots'
    assert params == ''
    assert fragment == ''
    assert query['intervention_ids[]'] == ['63258']
    assert 'start' in query
    assert 'end' in query
    assert query['apikey'] == ['test']
    assert query['format'] == ['json']

    response.json.return_value = {
        "availabletimeslots": [{"start": "2016-09-21 12:34:56"}, {"start": "2016-09-22 11:22:33"}]
    }
    mocked_request.return_value = response
    resp = app.get('/clicrdv/test/interventions/63258/dates/').json
    assert mocked_request.call_count == 2
    assert resp.get('err') == 0
    assert len(resp.get('data')) == 2
    assert resp['data'][0] == {'id': '2016-09-21', 'text': '21 September 2016'}
    assert resp['data'][1] == {'id': '2016-09-22', 'text': '22 September 2016'}

    response.json.return_value = {
        "availabletimeslots": [{"start": "2016-09-22 11:22:33"}, {"start": "2016-09-21 12:34:56"}]
    }  # will be sorted
    mocked_request.return_value = response
    resp = app.get('/clicrdv/test/interventions/63258/datetimes/').json
    assert mocked_request.call_count == 3
    assert resp.get('err') == 0
    assert len(resp.get('data')) == 2
    assert resp['data'][0] == {'id': '2016-09-21-12:34:56', 'text': '21 September 2016 12:34'}
    assert resp['data'][1] == {'id': '2016-09-22-11:22:33', 'text': '22 September 2016 11:22'}

    response.json.return_value = {
        "availabletimeslots": [{"start": "2016-09-21 12:34:56"}, {"start": "2016-09-21 11:22:33"}]
    }  # will be sorted
    mocked_request.return_value = response
    resp = app.get('/clicrdv/test/interventions/63258/2016-09-21/times').json
    assert mocked_request.call_count == 4
    url = mocked_request.call_args[0][1]
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
    query = urlparse.parse_qs(query, keep_blank_values=True)
    assert query['start'] == ['2016-09-21 00:00:00']
    assert query['end'] == ['2016-09-21 23:59:59']
    assert resp.get('err') == 0
    assert len(resp.get('data')) == 2
    assert resp['data'][0] == {'id': '11:22:33', 'text': '11:22'}
    assert resp['data'][1] == {'id': '12:34:56', 'text': '12:34'}


@mock.patch('passerelle.utils.Request.request')
def test_interventions_get_datetimes_error(mocked_request, app, connector):
    def raise_for_status():
        raise HTTPError("400 Client Error: Bad Request for url: xxx")

    response = mock.Mock()
    response.json.return_value = [
        {"error": "The intervention_ids parameter contains at least one invalid id"}
    ]
    response.raise_for_status = raise_for_status
    mocked_request.return_value = response

    resp = app.get('/clicrdv/test/interventions/63258/datetimes/').json
    assert len(resp.get('data')) == 0


@mock.patch('passerelle.utils.Request.request')
def test_cancel_appointment(mocked_request, app, connector):
    obj_type = ContentType.objects.get_for_model(ClicRdv)
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API', key='apiuser')
    AccessRight.objects.create(
        codename='can_manage_appointment', resource_type=obj_type, resource_pk=connector.pk, apiuser=apiuser
    )

    resp = app.get('/clicrdv/test/63258/cancel?apikey=apiuser').json
    assert mocked_request.call_count == 1
    assert resp['data']['success']


@mock.patch('passerelle.utils.Request.request')
def test_failed_cancel_appointment(mocked_request, app, connector):
    def raise_for_status():
        raise HTTPError("400 Client Error: Bad Request for url: xxx")

    response = mock.Mock()
    response.json.return_value = [{"msg": "cancel failed"}]
    response.raise_for_status = raise_for_status
    mocked_request.return_value = response
    obj_type = ContentType.objects.get_for_model(ClicRdv)
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API', key='apiuser')
    AccessRight.objects.create(
        codename='can_manage_appointment', resource_type=obj_type, resource_pk=connector.pk, apiuser=apiuser
    )
    resp = app.get('/clicrdv/test/63258/cancel?apikey=apiuser').json
    assert mocked_request.call_count == 1
    assert resp.get('err') == 0
    assert resp['data']
    assert 'cancel failed' in resp['data']['error']


@mock.patch('passerelle.utils.Request.request')
def test_failed_appointment_creation(mocked_request, app, connector):
    def raise_for_status():
        raise HTTPError("400 Client Error: Bad Request for url: xxx")

    response = mock.Mock()
    response.json.return_value = [{"msg": "creation failed"}]
    response.raise_for_status = raise_for_status
    mocked_request.return_value = response
    obj_type = ContentType.objects.get_for_model(ClicRdv)
    apiuser = ApiUser.objects.create(username='apiuser', keytype='API', key='apiuser')
    AccessRight.objects.create(
        codename='can_manage_appointment', resource_type=obj_type, resource_pk=connector.pk, apiuser=apiuser
    )

    data = {
        'fields': {
            'clicrdv_date_raw': '2017-01-01',
            'clicrdv_time_raw': '12:00:00',
            'clicrdv_fiche_str10': 'Test',
            'firstname': 'Foo',
            'lastname': 'Bar',
            'email': 'foobar@example.com',
        }
    }
    resp = app.post_json('/clicrdv/test/interventions/63258/create?apikey=apiuser', params=data).json
    assert resp['data']
    assert not resp['data']['success']
    assert 'creation failed' in resp['data']['error']
