from unittest import mock

import mellon.models
import pytest
import requests
from django.contrib.auth.models import User

from docbow_project.docbow.models import Delegation, DocbowProfile


def find_delegate_form(forms):
    for form in forms.values():
        if 'delegate-create' in form.fields:
            return form


@pytest.fixture
def a2settings(settings):
    settings.AUTHENTIC_URL = 'https://a2-url/'
    settings.AUTHENTIC_USER = 'user'
    settings.AUTHENTIC_PASSWORD = 'pass'
    return settings


@pytest.fixture
def user():
    user = User.objects.create(username='user', email='user@example.com')
    user.set_password('password')
    user.save()
    return user


class MockResp:
    def __init__(self, json=None, excp=None):
        self._json = json
        self._excp = excp

    def json(self):
        return self._json

    def raise_for_status(self):
        if self._excp:
            raise self._excp


@pytest.mark.django_db
def test_create_delegate_sso(a2settings, app, monkeypatch, users):
    a2settings.AUTHENTIC_ROLE = 'roleuuid'
    import mellon

    import docbow_project.docbow.utils

    mellon.models.Issuer.objects.create(entity_id=a2settings.AUTHENTIC_URL + 'idp/saml2/metadata')

    mock_resp1 = MockResp(json={'uuid': '1234'})
    mock_resp2 = MockResp()
    mock_post = mock.Mock(side_effect=[mock_resp1, mock_resp2])
    monkeypatch.setattr(docbow_project.docbow.utils.requests, 'post', mock_post)
    user = User.objects.get(username='user-1')
    app.login(username='user-1')
    resp = app.get('/profile/')
    delegate_form = find_delegate_form(resp.forms)
    delegate_form['delegate-first_name'] = 'some'
    delegate_form['delegate-last_name'] = 'delegate'
    delegate_form['delegate-email'] = 'delagate@exemple.com'
    resp = delegate_form.submit('delegate-create')

    assert resp.status_code == 302
    assert user.delegations_to.count()
    delegate = user.delegations_to.first().to
    assert delegate.first_name == 'some'
    assert delegate.last_name == 'delegate'
    assert delegate.email == 'delagate@exemple.com'

    assert delegate.saml_identifiers.count() == 1
    saml_id = delegate.saml_identifiers.first()
    assert saml_id.issuer.entity_id == a2settings.AUTHENTIC_URL + 'idp/saml2/metadata'
    assert saml_id.user == delegate
    assert saml_id.name_id == '1234'

    call_args = mock_post.call_args_list[0][1]
    json = call_args['json']
    assert json['first_name'] == delegate.first_name
    assert json['last_name'] == delegate.last_name
    assert json['email'] == delegate.email
    assert json['send_registration_email']
    assert json['send_registration_email_next_url'] == 'http://testserver/'

    call_args = mock_post.call_args_list[1][1]
    url = call_args['url']
    assert url == 'https://a2-url/api/roles/roleuuid/members/1234/'


@pytest.mark.django_db
def test_create_delegate_a2_failed(a2settings, app, monkeypatch, users):
    import docbow_project.docbow.utils

    mock_resp = MockResp(json={'errors': {'some': ['a2 error']}}, excp=requests.exceptions.RequestException())
    mock_post = mock.Mock(return_value=mock_resp)
    monkeypatch.setattr(docbow_project.docbow.utils.requests, 'post', mock_post)

    user = User.objects.get(username='user-1')
    app.login(username='user-1')
    resp = app.get('/profile/')
    delegate_form = find_delegate_form(resp.forms)
    delegate_form['delegate-first_name'] = 'some'
    delegate_form['delegate-last_name'] = 'delegate'
    delegate_form['delegate-email'] = 'delagate@exemple.com'
    resp = delegate_form.submit('delegate-create')

    assert 'a2 error' in resp.text
    assert not user.delegations_to.count()


@pytest.mark.django_db
def test_delete_delegate_sso(a2settings, client, monkeypatch, user):
    import docbow_project.docbow.profile_views

    mock_resp = MockResp(json={})
    mock_delete = mock.Mock(return_value=mock_resp)
    monkeypatch.setattr(docbow_project.docbow.utils.requests, 'delete', mock_delete)

    delegate = User.objects.create(
        first_name='john', last_name='doe', username='john.doe-1', email='john@localhost'
    )
    DocbowProfile.objects.create(user=delegate, is_guest=True)
    Delegation.objects.get_or_create(by=user, to=delegate)

    issuer = mellon.models.Issuer.objects.create(entity_id=a2settings.AUTHENTIC_URL + 'idp/saml2/metadata')
    mellon.models.UserSAMLIdentifier.objects.create(name_id='1234', issuer=issuer, user=delegate)

    client.login(username='user', password='password')
    client.post(
        '/profile/',
        {
            'delegate-delete-john.doe-1.x': True,
        },
    )
    assert not user.delegations_to.count()
    call_args = mock_delete.call_args[1]
    assert call_args['url'] == 'https://a2-url/api/users/1234'
