from unittest import mock

import pytest
from requests.exceptions import ConnectionError

import tests.utils
from passerelle.contrib.teamnet_axel.models import Link, TeamnetAxel
from passerelle.utils.jsonresponse import APIError


@pytest.fixture
def setup(db):
    return tests.utils.make_resource(
        TeamnetAxel,
        **{
            'slug': 'test',
            'wsdl_url': 'http://example.net/AXEL_WS/AxelWS.php?wsdl',
            'billing_regies': {
                "11": "EN2-CLASSE",
                "27": "EN10-FM",
                "37": "EN3-DONS",
                "31": "EN31-C.V.",
                "42": "EN29-RESTC",
                "43": "EN32-ENFAN",
                "38": "EN30-PRODD",
            },
        },
    )


AUTH_RESPONSE = '''
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE PORTAIL>
            <PORTAILSERVICE>
                <RESULTAT>
                    <TYPE>ConnexionCompteFamille</TYPE>
                    <STATUS>OK</STATUS>
                    <DATE>20/01/2018 17:47:25</DATE>
                    <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
                </RESULTAT>
                <DATA>
                    <PORTAIL>
                        <UTILISATEUR>
                            <LOGIN>8336A</LOGIN>
                            <ESTIDENTIFIE>{}</ESTIDENTIFIE>
                            <ESTBLOQUE>false</ESTBLOQUE>
                            <ESTCHANGEMENT_MDP_REQUIS>false</ESTCHANGEMENT_MDP_REQUIS>
                            <NBECHEC>0</NBECHEC>
                            <IDFAMILLE>8336</IDFAMILLE>
                            <IDPERSONNE>25239</IDPERSONNE>
                        </UTILISATEUR>
                    </PORTAIL>
                </DATA>
            </PORTAILSERVICE>
    '''.strip()

FAMILY_DATA_RESPONSE = '''
    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE PORTAIL>
    <PORTAILSERVICE>
        <RESULTAT>
            <TYPE>DonneesFamille</TYPE>
            <STATUS>OK</STATUS>
            <DATE>20/01/2015 17:47:25</DATE>
            <COMMENTAIRES><![CDATA[]]>
            </COMMENTAIRES>
        </RESULTAT>
        <DATA>
            <PORTAIL>
                <INDIVIDUS IDFAMILLE="8336" INDTYPE="1" IDINDIVIDU="25239"
                    CIVILITE="Monsieur" NOM="FONTAINE" PRENOM="Hugo" NAISSANCE="17/04/1991"
                    SEXE="M" TELPORTABLE="" TELFIXE="0123456789" MAIL="h.fontaine3@teamnet.fr"
                    TELBUREAUFIXE="" POSTEBUREAUFIXE="" NORUE="39" VOIE="AVENUE J. BREL"
                    ETAGE="1" RESIDENCE="" COMPLEMENT="" CODEPOSTAL="94550" VILLE="CHEVILLY LARUE"
                    EDIT="" DEMAT="N" ECOLE="" NIVEAU="" IDECOLE="" IDNIVEAU=""
                    SOUSCRIPTION="N" CSP="" NOMJF="" EMPNORUE="" EMPVOIE="" EMPETAGE=""
                    EMPRESIDENCE="" EMPCOMPLEMENT="" EMPCODEPOSTAL="" EMPVILLE=""
                    TELPORTBUREAU="" NBREENFANTS="" SITUATION="" NUMALLOC="" ORGAALLOC=""
                    AUTRE="" CAF="0" IDPAYS="77" IDENTTYPEFAMILLE="" ANNEE="2014" />
                <INDIVIDUS IDFAMILLE="8336" INDTYPE="3" IDINDIVIDU="25278"
                    CIVILITE="" NOM="FONTAINE" PRENOM="Javou" NAISSANCE="04/12/2014"
                    SEXE="M" TELPORTABLE="" TELFIXE="" MAIL="" TELBUREAUFIXE=""
                    POSTEBUREAUFIXE="" NORUE="" VOIE="" ETAGE="" RESIDENCE=""
                    COMPLEMENT="" CODEPOSTAL="" VILLE="" EDIT="N" DEMAT="N" ECOLE=""
                    NIVEAU="" IDECOLE="" IDNIVEAU="" SOUSCRIPTION="" CSP="" NOMJF=""
                    EMPNORUE="" EMPVOIE="" EMPETAGE="" EMPRESIDENCE="" EMPCOMPLEMENT=""
                    EMPCODEPOSTAL="" EMPVILLE="" TELPORTBUREAU="" NBREENFANTS=""
                    SITUATION="" NUMALLOC="" ORGAALLOC="" AUTRE="" CAF="0" IDPAYS="77"
                    IDENTTYPEFAMILLE="" ANNEE="2014" />
            </PORTAIL>
        </DATA>
    </PORTAILSERVICE>
    '''.strip()


INVOICE_RESPONSE = '''
    <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE PORTAIL>
    <PORTAILSERVICE>
        <RESULTAT>
            <TYPE>FacturesApayerRegie</TYPE>
            <STATUS>OK</STATUS>
            <DATE>02/01/2019 16:31:09</DATE>
            <COMMENTAIRES></COMMENTAIRES>
        </RESULTAT>
        <DATA>
            <PORTAIL>
                <FACTURES FEMISSION="02/02/2019" FECHEANCE="02/02/2019" FNUMERO="6456"
                    IDFACTURE="456" FLIBELLE="" FMONTANT="101" FENCAISSE="0" FPDF="O"
                    NOFACTURE="F201901456" RESTEAPAYER="0"/>
                <FACTURES FEMISSION="02/01/2019" FECHEANCE="02/01/2019" FNUMERO="6470"
                    IDFACTURE="470" FLIBELLE="" FMONTANT="75" FENCAISSE="0" FPDF="O"
                    NOFACTURE="F201902470" RESTEAPAYER="0"/>
            </PORTAIL>
        </DATA>
    </PORTAILSERVICE>
'''.strip()


INVOICE_HISTORY_RESPONSE = '''
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE PORTAIL>
<PORTAILSERVICE>
  <RESULTAT>
    <TYPE>HistoriqueFacturesRegie</TYPE>
    <STATUS>OK</STATUS>
    <DATE>13/06/2018 11:38:19</DATE>
    <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
  </RESULTAT>
  <DATA>
      <PORTAIL>
          <FACTURES FEMISSION="10/02/2018" FECHEANCE="22/12/2017" NOFACTURE="1080167" IDFACTURE="1080167" FLIBELLE="Restauration et Centre de loisirs Novembre 2017" FMONTANT="84.61" FENCAISSE="84.61" FPDF="O" RESTEAPAYER="0"/>
          <FACTURES FEMISSION="10/03/2018" FECHEANCE="27/02/2018" NOFACTURE="1089973" IDFACTURE="1089973" FLIBELLE="Restauration et Centre de loisirs Janvier 2018" FMONTANT="93.52" FENCAISSE="93.52" FPDF="O" RESTEAPAYER="0"/>
          <FACTURES FEMISSION="07/05/2018" FECHEANCE="28/04/2018" NOFACTURE="1099885" IDFACTURE="1099885" FLIBELLE="Restauration et Centre de loisirs Mars 2018" FMONTANT="106.82" FENCAISSE="106.82" FPDF="O" RESTEAPAYER="0"/>
          <FACTURES FEMISSION="09/06/2017" FECHEANCE="29/06/2017" NOFACTURE="1051741" IDFACTURE="1051741" FLIBELLE="Restauration et Centre de loisirs Mai 2017" FMONTANT="103.7" FENCAISSE="103.7" FPDF="O" RESTEAPAYER="0"/>
      </PORTAIL>
    </DATA>
</PORTAILSERVICE>
'''.strip()


INVOICE_PDF_RESPONSE = '''
<?xml version="1.0"?>
<PORTAILSERVICE>
  <RESULTAT>
    <TYPE>FacturesPDF</TYPE>
    <STATUS>OK</STATUS>
    <DATE>13/06/2018 13:37:02</DATE>
    <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
  </RESULTAT>
  <DATA>
    <PORTAIL>
      <PDF NOFACTURE="F201901456" FILE="dGhpcyBpcyBzaW1wbGUgZmlsZQ=="/>
    </PORTAIL>
  </DATA>
</PORTAILSERVICE>
'''.strip()


INVOICE_PAYMENT_RESPONSE = '''
<?xml version="1.0"?>
<PORTAILSERVICE>
  <RESULTAT>
    <TYPE>PaiementFactures</TYPE>
    <STATUS>OK</STATUS>
    <DATE>10/06/2018 20:44:58</DATE>
    <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
  </RESULTAT>
  <DATA>
    <PORTAIL/>
  </DATA>
</PORTAILSERVICE>
'''.strip()


def test_authentication_failed(app, setup):
    with mock.patch('passerelle.contrib.teamnet_axel.models.TeamnetAxel.get_family_id') as mocked:
        mocked.side_effect = APIError('authentication failed')
        NameID = 'abcd' * 8
        resp = app.get('/teamnet-axel/test/family/', params={'NameID': NameID})
        assert resp.json['err'] == 1
        assert resp.json['err_desc'] == 'authentication failed'


def test_missing_name_id(app, setup):
    resp = app.get('/teamnet-axel/test/family/', params={}, status=400)
    assert resp.json['err'] == 1
    assert resp.json['err_desc'] == "missing parameters: 'NameID'."


@mock.patch('passerelle.contrib.teamnet_axel.soap.get_client')
def test_endpoint_ping(soap_client, app, setup):
    soap_client.return_value = mock.Mock()
    resp = app.get('/teamnet-axel/test/ping/')
    assert resp.json['data']['ping'] == 'pong'


@mock.patch(
    'passerelle.contrib.teamnet_axel.soap.get_client',
    side_effect=ConnectionError('Mocked Request ConnectionError'),
)
def test_endpoint_ping_with_connection_error(soap_client, app, setup):
    resp = app.get('/teamnet-axel/test/ping/')
    assert resp.json['err_desc'] == 'Client Error: Mocked Request ConnectionError'


def get_client(authenticated=True):
    return mock.Mock(service=MockedService(authenticated=authenticated))


class MockedService(mock.Mock):
    def getData(self, streamId, xmlParams, user):
        if streamId == 'ConnexionCompteFamille':
            return AUTH_RESPONSE.format(str(self.authenticated).lower())
        elif streamId == 'DonneesFamille':
            return FAMILY_DATA_RESPONSE
        elif streamId == 'FacturesApayerRegie':
            return INVOICE_RESPONSE
        elif streamId == 'FacturesPDF':
            return INVOICE_PDF_RESPONSE
        elif streamId == 'HistoriqueFacturesRegie':
            return INVOICE_HISTORY_RESPONSE
        else:
            return INVOICE_PAYMENT_RESPONSE


def test_endpoint_auth(app, setup):
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar'}
        # test authentication failure
        client.return_value = get_client(authenticated=False)
        resp = app.get('/teamnet-axel/test/auth/', params=params)
        assert resp.json['data'] is False
        # tests authentication success
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/auth/', params=params)
        assert resp.json['data']['estidentifie'] is True
        assert resp.json['data']['idfamille'] == '8336'


def test_endpoint_family_link(app, setup):
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar'}
        # test without nameid
        resp = app.get('/teamnet-axel/test/family/link/', params=params, status=400)
        params['NameID'] = 'abcd' * 8
        # test authentication failure
        client.return_value = get_client(authenticated=False)
        resp = app.get('/teamnet-axel/test/family/link/', params=params)
        assert resp.json['err_desc'] == 'authentication failed'
        # tests authentication success
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/family/link/', params=params)
        assert resp.json['data']['estidentifie'] is True
        assert resp.json['data']['idfamille'] == '8336'
        assert Link.objects.count() == 1
        link = Link.objects.get()
        assert link.login == 'foo'
        assert link.pwd == 'bar'
        assert link.nameid == 'abcd' * 8


def test_endpoint_family_unlink(app, setup):
    params = {'login': 'foo', 'password': 'bar'}
    # test without nameid
    resp = app.get('/teamnet-axel/test/family/unlink/', params=params, status=400)
    nameid = 'abcd' * 8
    params['NameID'] = nameid
    # test unlinking success
    Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
    resp = app.get('/teamnet-axel/test/family/unlink/', params=params)
    assert resp.json['data']['login_was'][0] == 'foo'


def test_endpoint_family_view(app, setup):
    nameid = 'abcd' * 8
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar'}
        # test without nameid
        app.get('/teamnet-axel/test/family/', params=params, status=400)
        params['NameID'] = 'abcd' * 8
        # test multiple links error
        Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
        Link.objects.create(resource=setup, login='spam', pwd='ham', nameid=nameid)
        resp = app.get('/teamnet-axel/test/family/', params=params)
        assert resp.json['err_desc'] == 'multiple links'
        # test unsuccesful authentication
        Link.objects.get(resource=setup, login='spam', pwd='ham', nameid=nameid).delete()
        client.return_value = get_client(authenticated=False)
        resp = app.get('/teamnet-axel/test/family/', params=params)
        assert resp.json['err_desc'] == 'authentication failed'
        # test with successful authentication
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/family/', params=params)
        assert len(resp.json['data']['adults']) == 1
        assert len(resp.json['data']['children']) == 1
        assert resp.json['data']['family'] == '8336'
        # test filtering
        resp = app.get('/teamnet-axel/test/family/adults/', params=params)
        data = resp.json['data']
        assert data[0]['address'] == '39, AVENUE J. BREL, 94550 CHEVILLY LARUE'
        assert data[0]['idfamille'] == '8336'


def test_endpoint_invoices(app, setup):
    nameid = 'abcd' * 8
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar', 'NameID': nameid}
        Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/regie/42/invoices/', params=params)
        data = resp.json['data']
        assert len(data) == 2
        assert data[0]['id'] == '8336-F201901456'
        assert data[0]['IDFACTURE'] == '456'
        assert data[0]['total_amount'] == '101'
        assert data[0]['has_pdf'] is True


def test_endpoint_invoices_history(app, setup):
    nameid = 'abcd' * 8
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar', 'NameID': nameid}
        Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/regie/42/invoices/history/', params=params)
        data = resp.json['data']
        assert len(data) == 4


def test_endpoint_invoice_id(app, setup):
    nameid = 'abcd' * 8
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar', 'NameID': nameid}
        Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/regie/42/invoice/8336-F201901456/', params=params)
        data = resp.json['data']
        assert data['id'] == '8336-F201901456'
        assert data['IDFACTURE'] == '456'
        assert data['total_amount'] == '101'
        assert data['has_pdf'] is True


def test_endpoint_invoice_pdf(app, setup):
    nameid = 'abcd' * 8
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'login': 'foo', 'password': 'bar', 'NameID': nameid}
        Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
        client.return_value = get_client(authenticated=True)
        resp = app.get('/teamnet-axel/test/regie/42/invoice/8336-F201901456/pdf/', params=params)
        assert resp.headers['Content-Disposition'] == 'attachment; filename="8336-F201901456.pdf"'


def test_endpoint_pay_invoice(app, setup):
    nameid = 'abcd' * 8
    with mock.patch('passerelle.contrib.teamnet_axel.soap.Client') as client:
        params = {'transaction_id': '1234' * 8, 'transaction_date': '2018-06-13T11:43:12'}
        Link.objects.create(resource=setup, login='foo', pwd='bar', nameid=nameid)
        client.return_value = get_client(authenticated=True)
        # test with an invalid invoice id
        resp = app.post_json('/teamnet-axel/test/regie/42/invoice/ham-egg/pay/', params=params)
        assert resp.json['data'] is False
        # test with a valid invoice id
        resp = app.post_json('/teamnet-axel/test/regie/42/invoice/8336-F201901456/pay/', params=params)
        assert resp.json['data'] is True
