from unittest import mock

import pytest

import tests.utils
from passerelle.apps.vivaticket.models import VivaTicket
from passerelle.utils.jsonresponse import APIError

ERROR_RESPONSE = """{
  "Message": "Une erreur s’est produite."
}"""

KEY_RESPONSE = """{
  "Key": "86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074",
  "ReturnCode": 0,
  "Error": null
}"""

EVENTS_RESPONSE = """{
  "ListOfSettings": [
    {
      "Code": "01",
      "Label": "Visite Musée"
    },
    {
      "Code": "02",
      "Label": "Visite Exposition"
    },
    {
      "Code": "03",
      "Label": "Visite Musée + Atelier"
    },
    {
      "Code": "04",
      "Label": "Visite Musée + Expo + Atelier"
    },
    {
      "Code": "06",
      "Label": "Atelier"
    },
    {
      "Code": "05",
      "Label": "Auditorium"
    },
    {
      "Code": "10",
      "Label": "Prévente"
    }
  ],
  "ReturnCode": 0,
  "Error": null
}"""

ROOMS_RESPONSE = """{
  "ListOfSettings": [
    {
      "Code": "001",
      "Label": "Collections Permanentes"
    },
    {
      "Code": "002",
      "Label": "Exposition"
    },
    {
      "Code": "003",
      "Label": "Salle Pédagogique 1"
    },
    {
      "Code": "004",
      "Label": "Salle Pédagogique 2"
    },
    {
      "Code": "006",
      "Label": "Laboratoire"
    },
    {
      "Code": "005",
      "Label": "Auditorium"
    }
  ],
  "ReturnCode": 0,
  "Error": null
}"""

THEMES_RESPONSE = """{
  "ListOfSettings": [
    {
      "Code": "A0001",
      "Label": "Atelier scolaire"
    },
    {
      "Code": "A0002",
      "Label": "Atelier Enfant - Individuel"
    },,
    {
      "Code": "C0001",
      "Label": "Conférence - Sujet 1"
    },
    {
      "Code": "C0002",
      "Label": "Conférence - Sujet 2"
    },
    {
      "Code": "R0001",
      "Label": "Petit Déjeuner"
    },
    {
      "Code": "R0002",
      "Label": "Déjeuner"
    },
    {
      "Code": "S0001",
      "Label": "Spectacle - Titre  1"
    },
    {
      "Code": "S0002",
      "Label": "Spectacle - Titre  2"
    },
    {
      "Code": "S0003",
      "Label": "Spectacle - Titre  3"
    },
    {
      "Code": "V001",
      "Label": "Visite Guidée Musée - Individuel"
    },
    {
      "Code": "V002",
      "Label": "Visite Guidée Musée - Groupe"
    },
    {
      "Code": "V003",
      "Label": "Visite Guidée Musée - Scolaire"
    },
    {
      "Code": "V004",
      "Label": "Visite Libre Musée - Groupe"
    },
    {
      "Code": "V0041",
      "Label": "Visite Libre Musée - Scolaire"
    },
    {
      "Code": "V005",
      "Label": "Visite Guidée Expo - Individuel"
    },
    {
      "Code": "V006",
      "Label": "Visite Guidée Expo - Groupe"
    },
    {
      "Code": "V007",
      "Label": "Visite Guidée Expo - Scolaire"
    },
    {
      "Code": "V008",
      "Label": "Visite Libre Expo - Groupe"
    },
    {
      "Code": "V009",
      "Label": "Visite Libre Expo - Scolaire"
    }
  ],
  "ReturnCode": 0,
  "Error": null
}"""

CONTACT_RESPONSE = """{
  "InternalCode": "0000000273",
  "LastName": "Bar",
  "FirstName": "Foo",
  "SocialReason": null,
  "Address1": null,
  "Address2": null,
  "Address3": null,
  "Address4": null,
  "ZipCode": null,
  "City": null,
  "Country": "FR",
  "Email": "foo@example.com",
  "Phone": null,
  "Mobile": null,
  "ExternalCode": "foo",
  "Civility": null
}"""

BOOKING_RESPONSE = """{
   "bookingCode": "II0000013",
   "ReturnCode": 0,
   "Error": null
}"""

SCHOOL_LEVELS_RESPONSE = """{
  "ListOfSettings": [
    {
      "Code": "00",
      "Label": "Maternelle"
    },
    {
      "Code": "01",
      "Label": "Cp"
    },
    {
      "Code": "02",
      "Label": "Ce1-Ce2"
    },
    {
      "Code": "03",
      "Label": "Cm1-Cm2"
    }
  ],
  "ReturnCode": 0,
  "Error": null
}"""


@pytest.fixture
def connector(db):
    return tests.utils.setup_access_rights(
        VivaTicket.objects.create(
            slug='test', login='foo', password='bar', url='http://example.net/vivaticket'
        )
    )


@mock.patch('passerelle.utils.Request.post')
def test_get_api_key(mocked_post, app, connector):
    with pytest.raises(APIError):
        mocked_post.return_value = tests.utils.FakedResponse(content=ERROR_RESPONSE, ok=False)
        connector.get_apikey()
    mocked_post.return_value = tests.utils.FakedResponse(content=KEY_RESPONSE, ok=True)
    connector.get_apikey()
    assert mocked_post.call_count == 2
    assert "Connect/PostConnect" in mocked_post.call_args[0][0]
    assert mocked_post.call_args[1]['json']['Login'] == 'foo'
    assert mocked_post.call_args[1]['json']['Password'] == 'bar'
    # make sure the key from cache is used
    connector.get_apikey()
    assert mocked_post.call_count == 2
    connector.get_apikey(True)
    assert mocked_post.call_count == 3


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.get')
def test_get_events(mocked_get, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content=EVENTS_RESPONSE, status_code=200)
    mocked_post.return_value = tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200)
    result = tests.utils.endpoint_get('/vivaticket/test/events', app, connector, 'events')
    assert mocked_post.call_count == 1
    assert mocked_get.call_args[1]['params']['key'] == "86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074"
    assert 'data' in result.json
    for item in result.json['data']:
        assert 'id' in item
        assert 'text' in item


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.get')
def test_get_events_with_expired_key(mocked_get, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content=EVENTS_RESPONSE, status_code=401)
    mocked_post.return_value = tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200)
    tests.utils.endpoint_get('/vivaticket/test/events', app, connector, 'events')
    assert mocked_post.call_count == 2


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.get')
def test_get_rooms(mocked_get, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content=ROOMS_RESPONSE, status_code=200)
    mocked_post.return_value = tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200)
    result = tests.utils.endpoint_get('/vivaticket/test/rooms', app, connector, 'rooms')
    assert mocked_get.call_args[1]['params']['key'] == '86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074'
    assert 'data' in result.json
    for item in result.json['data']:
        assert 'id' in item
        assert 'text' in item
    result = tests.utils.endpoint_get(
        '/vivaticket/test/rooms', app, connector, 'rooms', params={'event': '02'}
    )
    assert mocked_get.call_args[1]['params']['eventCategory'] == '02'


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.get')
def test_get_themes(mocked_get, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content=ROOMS_RESPONSE, status_code=200)
    mocked_post.return_value = tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200)
    result = tests.utils.endpoint_get('/vivaticket/test/themes', app, connector, 'themes')
    assert mocked_get.call_args[1]['params']['key'] == '86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074'
    assert 'data' in result.json
    for item in result.json['data']:
        assert 'id' in item
        assert 'text' in item
    result = tests.utils.endpoint_get(
        '/vivaticket/test/themes', app, connector, 'themes', params={'room': '001'}
    )
    assert mocked_get.call_args[1]['params']['room'] == '001'


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.get')
def test_get_school_levels(mocked_get, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content=SCHOOL_LEVELS_RESPONSE, status_code=200)
    mocked_post.return_value = tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200)
    result = tests.utils.endpoint_get('/vivaticket/test/school-levels', app, connector, 'school-levels')
    assert mocked_get.call_args[1]['params']['key'] == '86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074'
    assert 'data' in result.json
    for item in result.json['data']:
        assert 'id' in item
        assert 'text' in item


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.put')
@mock.patch('passerelle.utils.Request.get')
def test_get_or_create_contact(mocked_get, mocked_put, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content='', ok=False)
    mocked_post.side_effect = [
        tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200),
        tests.utils.FakedResponse(
            content='{"InternalCode": "0000000273", "ReturnCode": 0, "Error": null}', status_code=200
        ),
    ]
    assert connector.get_or_create_contact({'email': 'foo@example.com'}) == {'InternalCode': '0000000273'}
    assert mocked_get.call_args[1]['params']['externalCode'] == 'b48def645758b95537d4'
    assert mocked_get.call_args[1]['params']['email'] == 'foo@example.com'
    mocked_put.assert_not_called()
    assert mocked_post.call_args[1]['json']['Key'] == '86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074'
    assert mocked_post.call_args[1]['json']['Contact']['Email'] == 'foo@example.com'
    assert mocked_post.call_args[1]['json']['Contact']['ExternalCode'] == 'b48def645758b95537d4'
    mocked_post.return_value = tests.utils.FakedResponse(
        content='{"InternalCode": "0000000273", "ReturnCode": 0, "Error": null}', status_code=200
    )
    mocked_get.return_value = tests.utils.FakedResponse(content=CONTACT_RESPONSE, ok=True)
    mocked_put.return_value = tests.utils.FakedResponse(content='', status_code=204)
    connector.get_or_create_contact({'email': 'foo@example.com'})
    mocked_put.assert_called()
    assert mocked_put.call_args[1]['params']['id'] == '0000000273'
    assert mocked_put.call_args[1]['json']['Key'] == '86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074'
    assert mocked_put.call_args[1]['json']['Contact']['Email'] == 'foo@example.com'


@mock.patch('passerelle.utils.Request.post')
@mock.patch('passerelle.utils.Request.put')
@mock.patch('passerelle.utils.Request.get')
def test_book(mocked_get, mocked_put, mocked_post, app, connector):
    mocked_get.return_value = tests.utils.FakedResponse(content=CONTACT_RESPONSE, status_code=200)
    url = tests.utils.generic_endpoint_url('vivaticket', 'book')
    payload = {'id': 'formid', 'email': 'foo@example.com'}
    response = app.post_json(url, params=payload, status=400)
    payload['start_datetime'] = '2019-01-15'
    payload['end_datetime'] = '2019-01-15'
    payload['event'] = '01'
    payload['theme'] = '001'
    payload['room'] = 'v001'
    payload['school_level'] = '01'
    payload['quantity'] = '01'
    payload['booking_comment'] = 'Booking comment'
    payload['room_comment'] = 'Room comment'
    payload['form_url'] = "http://mysite.com/form/id/"
    response = app.post_json(url, params=payload, status=400)
    assert "does not match '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}$" in response.json['err_desc']
    payload['start_datetime'] = '2019-01-15T10:00'
    payload['end_datetime'] = '2019-01-15T11:00'
    # first return the API key, then the result of the booking creation
    mocked_post.side_effect = [
        tests.utils.FakedResponse(content=KEY_RESPONSE, status_code=200),
        tests.utils.FakedResponse(content=BOOKING_RESPONSE),
        Exception(),
    ]
    response = app.post_json(url, params=payload)
    assert mocked_post.call_args[1]['json']['Key'] == '86569D0CA1B1CBEF8D77DD5BDC9F5CBAE5C99074'
    assert mocked_post.call_args[1]['json']['Booking']['externalCode'] == 'formid'
    assert mocked_post.call_args[1]['json']['Booking']['startDateTime'] == '2019-01-15T10:00'
    assert mocked_post.call_args[1]['json']['Booking']['endDateTime'] == '2019-01-15T11:00'
    assert mocked_post.call_args[1]['json']['Booking']['comment'] == 'Booking comment'
    assert mocked_post.call_args[1]['json']['Booking']['roomList'][0]['comment'] == 'Room comment'
    assert mocked_post.call_args[1]['json']['Booking']['roomList'][0]['quantity'] == 1
    assert mocked_post.call_args[1]['json']['Booking']['roomList'][0]['schoolLevelCode'] == '01'
    assert mocked_post.call_args[1]['json']['Booking']['contact'] == {'InternalCode': '0000000273'}
    assert mocked_post.call_args[1]['headers'] == {'X-Vivaticket-Form-URL': 'http://mysite.com/form/id/'}
    assert response.json['data']['bookingCode'] == 'II0000013'

    # no more need to fake key response because the key is in cache
    mocked_post.side_effect = [
        tests.utils.FakedResponse(text='This external booking code already exists.', ok=False)
    ]
    response = app.post_json(url, params=payload)
    assert response.json['err'] == 1
    assert response.json['err_desc'] == 'This external booking code already exists.'
    assert response.json['err_class'] == 'passerelle.utils.jsonresponse.APIError'
