import pytest
from django.conf import settings
from django.core import mail as djmail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
from django_scopes import scope
from rest_framework.authtoken.models import Token

from pretalx.submission.models import SubmissionStates


@pytest.mark.django_db
def test_can_see_submission_list(speaker_client, submission):
    response = speaker_client.get(submission.event.urls.user_submissions, follow=True)
    assert response.status_code == 200
    assert submission.title in response.content.decode()


@pytest.mark.django_db
def test_can_see_submission(speaker_client, submission):
    response = speaker_client.get(submission.urls.user_base, follow=True)
    assert response.status_code == 200
    assert submission.title in response.content.decode()


@pytest.mark.django_db
def test_cannot_see_other_submission(speaker_client, other_submission):
    response = speaker_client.get(other_submission.urls.user_base, follow=True)
    assert response.status_code == 404


@pytest.mark.django_db
def test_can_confirm_submission(speaker_client, accepted_submission):
    response = speaker_client.get(accepted_submission.urls.confirm, follow=True)
    accepted_submission.refresh_from_db()
    assert response.status_code == 200
    assert accepted_submission.state == SubmissionStates.ACCEPTED
    response = speaker_client.post(accepted_submission.urls.confirm, follow=True)
    accepted_submission.refresh_from_db()
    assert response.status_code == 200
    assert accepted_submission.state == SubmissionStates.CONFIRMED


@pytest.mark.django_db
def test_can_reconfirm_submission(speaker_client, accepted_submission):
    accepted_submission.state = SubmissionStates.CONFIRMED
    accepted_submission.save()
    response = speaker_client.get(accepted_submission.urls.confirm, follow=True)
    accepted_submission.refresh_from_db()
    assert response.status_code == 200
    assert accepted_submission.state == SubmissionStates.CONFIRMED


@pytest.mark.django_db
def test_cannot_confirm_rejected_submission(other_speaker_client, rejected_submission):
    rejected_submission.state = SubmissionStates.REJECTED
    rejected_submission.save()
    response = other_speaker_client.get(rejected_submission.urls.confirm, follow=True)
    rejected_submission.refresh_from_db()
    assert response.status_code == 200
    assert rejected_submission.state == SubmissionStates.REJECTED


@pytest.mark.django_db
def test_can_withdraw_submission(speaker_client, submission):
    response = speaker_client.get(submission.urls.withdraw, follow=True)
    submission.refresh_from_db()
    assert response.status_code == 200
    assert submission.state == SubmissionStates.SUBMITTED
    response = speaker_client.post(submission.urls.withdraw, follow=True)
    submission.refresh_from_db()
    assert response.status_code == 200
    assert submission.state == SubmissionStates.WITHDRAWN


@pytest.mark.django_db
def test_cannot_withdraw_accepted_submission(speaker_client, accepted_submission):
    response = speaker_client.get(accepted_submission.urls.withdraw, follow=True)
    accepted_submission.refresh_from_db()
    assert response.status_code == 200
    assert accepted_submission.state == SubmissionStates.ACCEPTED


@pytest.mark.django_db
def test_can_edit_submission(speaker_client, submission, resource, other_resource):
    with scope(event=submission.event):
        assert submission.resources.count() == 2
        resource_one = submission.resources.first()
        resource_two = submission.resources.last()
        assert submission.title in str(resource_one)
        f = SimpleUploadedFile('testfile.txt', b'file_content')
        data = {
            'title': 'Ein ganz neuer Titel',
            'submission_type': submission.submission_type.pk,
            'content_locale': submission.content_locale,
            'description': submission.description,
            'abstract': submission.abstract,
            'notes': submission.notes,
            'slot_count': submission.slot_count,
            'resource-0-id': resource_one.id,
            'resource-0-description': 'new resource name',
            'resource-0-resource': resource_one.resource,
            'resource-1-id': resource_two.id,
            'resource-1-DELETE': True,
            'resource-1-description': resource_two.description,
            'resource-1-resource': resource_two.resource,
            'resource-2-id': '',
            'resource-2-description': 'new resource',
            'resource-2-resource': f,
            'resource-TOTAL_FORMS': 3,
            'resource-INITIAL_FORMS': 2,
            'resource-MIN_NUM_FORMS': 0,
            'resource-MAX_NUM_FORMS': 1000,
        }
    response = speaker_client.post(submission.urls.user_base, follow=True, data=data)
    assert response.status_code == 200
    with scope(event=submission.event):
        assert submission.resources.count() == 2
        submission.refresh_from_db()
        resource_one.refresh_from_db()
        new_resource = submission.resources.exclude(pk=resource_one.pk).first()
        assert submission.title == 'Ein ganz neuer Titel', response.content.decode()
        assert submission.resources.count() == 2
        assert new_resource.description == 'new resource'
        assert new_resource.resource.read() == b'file_content'
        assert not submission.resources.filter(pk=resource_two.pk).exists()


@pytest.mark.django_db
def test_can_edit_slot_count(speaker_client, submission):
    with scope(event=submission.event):
        submission.event.settings.present_multiple_times = True
        data = {
            'title': 'Ein ganz neuer Titel',
            'submission_type': submission.submission_type.pk,
            'content_locale': submission.content_locale,
            'description': submission.description,
            'abstract': submission.abstract,
            'notes': submission.notes,
            'slot_count': 13,
            'resource-TOTAL_FORMS': 0,
            'resource-INITIAL_FORMS': 0,
            'resource-MIN_NUM_FORMS': 0,
            'resource-MAX_NUM_FORMS': 1000,
        }
    response = speaker_client.post(submission.urls.user_base, follow=True, data=data)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.slot_count == 13


@pytest.mark.django_db
def test_cannot_edit_confirmed_slot_count(speaker_client, confirmed_submission):
    submission = confirmed_submission
    submission.event.settings.present_multiple_times = True
    with scope(event=submission.event):
        data = {
            'title': 'Ein ganz neuer Titel',
            'submission_type': submission.submission_type.pk,
            'content_locale': submission.content_locale,
            'description': submission.description,
            'abstract': submission.abstract,
            'notes': submission.notes,
            'slot_count': 13,
            'resource-TOTAL_FORMS': 0,
            'resource-INITIAL_FORMS': 0,
            'resource-MIN_NUM_FORMS': 0,
            'resource-MAX_NUM_FORMS': 1000,
        }
    response = speaker_client.post(submission.urls.user_base, follow=True, data=data)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.slot_count != 13


@pytest.mark.django_db
def test_cannot_edit_rejected_submission(other_speaker_client, rejected_submission):
    title = rejected_submission.title
    data = {
        'title': 'Ein ganz neuer Titel',
        'submission_type': rejected_submission.submission_type.pk,
        'content_locale': rejected_submission.content_locale,
        'description': rejected_submission.description,
        'abstract': rejected_submission.abstract,
        'notes': rejected_submission.notes,
        'resource-TOTAL_FORMS': 0,
        'resource-INITIAL_FORMS': 0,
        'resource-MIN_NUM_FORMS': 0,
        'resource-MAX_NUM_FORMS': 1000,
    }
    response = other_speaker_client.post(
        rejected_submission.urls.user_base, follow=True, data=data
    )
    assert response.status_code == 200
    rejected_submission.refresh_from_db()
    assert rejected_submission.title == title


@pytest.mark.django_db
def test_can_edit_submission_type(speaker_client, submission, event):
    with scope(event=submission.event):
        new_type = event.submission_types.create(name='Other', default_duration=13)
        data = {
            'title': 'Ein ganz neuer Titel',
            'submission_type': new_type.pk,
            'content_locale': submission.content_locale,
            'description': submission.description,
            'abstract': submission.abstract,
            'notes': submission.notes,
            'resource-TOTAL_FORMS': 0,
            'resource-INITIAL_FORMS': 0,
            'resource-MIN_NUM_FORMS': 0,
            'resource-MAX_NUM_FORMS': 1000,
        }
    response = speaker_client.post(submission.urls.user_base, follow=True, data=data)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.submission_type == new_type


@pytest.mark.django_db
def test_cannot_edit_submission_type_after_acceptance(speaker_client, submission, event):
    with scope(event=submission.event):
        submission.accept()
        new_type = event.submission_types.create(name='Other', default_duration=13)
        data = {
            'title': 'Ein ganz neuer Titel',
            'submission_type': new_type.pk,
            'content_locale': submission.content_locale,
            'description': submission.description,
            'abstract': submission.abstract,
            'notes': submission.notes,
            'resource-TOTAL_FORMS': 0,
            'resource-INITIAL_FORMS': 0,
            'resource-MIN_NUM_FORMS': 0,
            'resource-MAX_NUM_FORMS': 1000,
        }
    response = speaker_client.post(submission.urls.user_base, follow=True, data=data)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.submission_type != new_type


@pytest.mark.django_db
def test_can_edit_profile(speaker, event, speaker_client):
    response = speaker_client.post(
        event.urls.user,
        data={
            'name': 'Lady Imperator',
            'biography': 'Ruling since forever.',
            'form': 'profile',
        },
        follow=True,
    )
    assert response.status_code == 200
    with scope(event=event):
        speaker.refresh_from_db()
        assert speaker.profiles.get(event=event).biography == 'Ruling since forever.'
        assert speaker.name == 'Lady Imperator'


@pytest.mark.django_db
def test_can_change_api_token(speaker, event, speaker_client):
    speaker.regenerate_token()
    old_token = Token.objects.filter(user=speaker).first().key
    response = speaker_client.post(
        event.urls.user,
        data={
            'form': 'token',
        },
        follow=True,
    )
    assert response.status_code == 200
    new_token = Token.objects.filter(user=speaker).first().key
    assert new_token != old_token


@pytest.mark.django_db
def test_must_provide_availabilities(speaker, event, speaker_client):
    event.settings.cfp_require_availabilities = True
    response = speaker_client.post(
        event.urls.user,
        data={
            'name': 'Lady Imperator',
            'biography': 'Ruling since forever.',
            'form': 'profile',
        },
        follow=True,
    )
    assert response.status_code == 200
    with scope(event=event):
        speaker.refresh_from_db()
        assert speaker.profiles.get(event=event).biography != 'Ruling since forever.'
    response = speaker_client.post(
        event.urls.user,
        data={
            'name': 'Lady Imperator',
            'biography': 'Ruling since forever.',
            'form': 'profile',
            'availabilities': '{"availabilities": []}',
        },
        follow=True,
    )
    assert response.status_code == 200
    with scope(event=event):
        speaker.refresh_from_db()
        assert speaker.profiles.get(event=event).biography != 'Ruling since forever.'


@pytest.mark.django_db
def test_can_edit_login_info(speaker, event, speaker_client):
    response = speaker_client.post(
        event.urls.user,
        data={
            'old_password': 'speakerpwd1!',
            'email': 'new_email@speaker.org',
            'password': '',
            'password_repeat': '',
            'form': 'login',
        },
        follow=True,
    )
    assert response.status_code == 200
    speaker.refresh_from_db()
    assert speaker.email == 'new_email@speaker.org'


@pytest.mark.django_db
def test_can_edit_login_info_wrong_password(speaker, event, speaker_client):
    response = speaker_client.post(
        event.urls.user,
        data={
            'old_password': 'speakerpwd23!',
            'email': 'new_email@speaker.org',
            'password': '',
            'password_repeat': '',
            'form': 'login',
        },
        follow=True,
    )
    assert response.status_code == 200
    speaker.refresh_from_db()
    assert speaker.email != 'new_email@speaker.org'


@pytest.mark.django_db
def test_can_edit_and_update_speaker_answers(
    speaker,
    event,
    speaker_question,
    speaker_boolean_question,
    speaker_client,
    speaker_text_question,
    speaker_file_question,
):
    with scope(event=event):
        answer = speaker.answers.filter(question_id=speaker_question.pk).first()
        assert not answer
    f = SimpleUploadedFile('testfile.txt', b'file_content')
    response = speaker_client.post(
        event.urls.user,
        data={
            f'question_{speaker_question.id}': 'black as the night',
            f'question_{speaker_boolean_question.id}': 'True',
            f'question_{speaker_file_question.id}': f,
            f'question_{speaker_text_question.id}': 'Green is totally the best color.',
            'form': 'questions',
        },
        follow=True,
    )
    assert response.status_code == 200

    with scope(event=event):
        answer = speaker.answers.get(question_id=speaker_question.pk)
        assert answer.answer == 'black as the night'
        assert speaker.answers.get(question_id=speaker_boolean_question.pk).answer == 'True'
        assert (
            speaker.answers.get(question_id=speaker_text_question.pk).answer
            == 'Green is totally the best color.'
        )

        file_answer = speaker.answers.get(question_id=speaker_file_question.pk)
        assert file_answer.answer.startswith('file://')
        assert file_answer.answer_file.read() == b'file_content'
        assert (settings.MEDIA_ROOT / file_answer.answer_file.name).exists()

    response = speaker_client.post(
        event.urls.user,
        data={
            f'question_{speaker_question.id}': 'green as the sky',
            'form': 'questions',
        },
        follow=True,
    )
    assert response.status_code == 200
    with scope(event=event):
        answer.refresh_from_db()
        assert answer.answer == 'green as the sky'


@pytest.mark.django_db
def test_cannot_delete_profile_on_first_try(speaker, event, speaker_client):
    with scope(event=event):
        assert speaker.profiles.get(event=event).biography != ''
    response = speaker_client.post(event.urls.user_delete, follow=True)
    assert response.status_code == 200
    with scope(event=event):
        speaker.refresh_from_db()
        assert speaker.profiles.get(event=event).biography != ''
        assert speaker.name != 'Deleted User'


@pytest.mark.django_db
def test_can_delete_profile(speaker, event, speaker_client):
    with scope(event=event):
        assert speaker.profiles.get(event=event).biography != ''
    response = speaker_client.post(
        event.urls.user_delete, data={'really': True}, follow=True
    )
    assert response.status_code == 200
    with scope(event=event):
        speaker.refresh_from_db()
        assert speaker.profiles.get(event=event).biography == ''
        assert speaker.name == 'Deleted User'
        assert speaker.email.startswith('deleted_user')
        assert speaker.email.endswith('@localhost')


@pytest.mark.django_db
def test_can_change_locale(multilingual_event, client):
    first_response = client.get(multilingual_event.cfp.urls.public, follow=True)
    assert 'submission' in first_response.content.decode()
    assert 'Einreichung' not in first_response.content.decode()
    second_response = client.get(
        reverse('cfp:locale.set', kwargs={'event': multilingual_event.slug})
        + f'?locale=de&next=/{multilingual_event.slug}/',
        follow=True,
    )
    assert 'Einreichung' in second_response.content.decode()


@pytest.mark.django_db
def test_persists_changed_locale(multilingual_event, orga_user, orga_client):
    assert orga_user.locale == 'en'
    response = orga_client.get(
        reverse('cfp:locale.set', kwargs={'event': multilingual_event.slug})
        + f'?locale=de&next=/{multilingual_event.slug}/',
        follow=True,
    )
    orga_user.refresh_from_db()
    assert response.status_code == 200
    assert orga_user.locale == 'de'


@pytest.mark.django_db
def test_can_invite_speaker(speaker_client, submission):
    djmail.outbox = []
    response = speaker_client.get(
        submission.urls.invite, follow=True, data={'email': 'invalidemail'}
    )
    assert response.status_code == 200
    data = {
        'speaker': 'other@speaker.org',
        'subject': 'Please join!',
        'text': 'C\'mon, it will be fun!',
    }
    response = speaker_client.post(submission.urls.invite, follow=True, data=data)
    assert response.status_code == 200
    assert len(djmail.outbox) == 1
    assert djmail.outbox[0].to == ['other@speaker.org']


@pytest.mark.django_db
def test_can_accept_invitation(orga_client, submission):
    assert submission.speakers.count() == 1
    response = orga_client.post(submission.urls.accept_invitation, follow=True)
    submission.refresh_from_db()
    assert response.status_code == 200
    assert submission.speakers.count() == 2


@pytest.mark.django_db
def test_wrong_acceptance_link(orga_client, submission):
    assert submission.speakers.count() == 1
    response = orga_client.post(
        submission.urls.accept_invitation + 'olololol', follow=True
    )
    submission.refresh_from_db()
    assert response.status_code == 404
    assert submission.speakers.count() == 1


@pytest.mark.django_db
@pytest.mark.parametrize('request_availability', (True, False))
def test_submission_accept(speaker_client, submission, request_availability):
    submission.event.settings.cfp_request_availabilities = request_availability
    submission.state = SubmissionStates.ACCEPTED
    submission.save()

    response = speaker_client.post(submission.urls.confirm, follow=True)
    submission.refresh_from_db()

    assert response.status_code == 200
    assert submission.state == SubmissionStates.CONFIRMED


@pytest.mark.django_db
def test_submission_accept_with_missing_availability(speaker_client, submission):
    submission.event.settings.cfp_request_availabilities = True
    submission.event.settings.cfp_require_availabilities = True
    submission.state = SubmissionStates.ACCEPTED
    submission.save()

    response = speaker_client.post(submission.urls.confirm, follow=True)
    submission.refresh_from_db()

    assert response.status_code == 200
    assert submission.state == SubmissionStates.ACCEPTED


@pytest.mark.django_db
def test_submission_accept_nologin(client, submission):
    submission.state = SubmissionStates.ACCEPTED
    submission.save()

    response = client.post(submission.urls.confirm, follow=True)
    submission.refresh_from_db()

    assert response.status_code == 200
    assert response.redirect_chain[-1][1] == 302
    assert 'login/?next=' in response.redirect_chain[-1][0]
    assert submission.state == SubmissionStates.ACCEPTED


@pytest.mark.django_db
def test_submission_accept_wrong_code(client, submission):
    submission.state = SubmissionStates.ACCEPTED
    submission.save()

    assert submission.code in submission.urls.confirm
    response = client.post(
        submission.urls.confirm.replace(submission.code, "foo"), follow=True
    )

    assert response.status_code == 200
    assert response.redirect_chain[-1][1] == 302
    assert 'login/?next=' in response.redirect_chain[-1][0]


@pytest.mark.django_db
def test_submission_withdraw(speaker_client, submission):
    djmail.outbox = []
    submission.state = SubmissionStates.SUBMITTED
    submission.save()

    response = speaker_client.post(submission.urls.withdraw, follow=True)
    assert response.status_code == 200
    submission.refresh_from_db()
    assert submission.state == SubmissionStates.WITHDRAWN
    assert len(djmail.outbox) == 0


@pytest.mark.django_db
def test_submission_withdraw_if_accepted(speaker_client, submission):
    djmail.outbox = []
    with scope(event=submission.event):
        submission.accept()

    response = speaker_client.post(submission.urls.withdraw, follow=True)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.state == SubmissionStates.WITHDRAWN
        assert len(djmail.outbox) == 1


@pytest.mark.django_db
def test_submission_withdraw_if_confirmed(speaker_client, submission):
    with scope(event=submission.event):
        submission.accept()
        submission.confirm()

    response = speaker_client.post(submission.urls.withdraw, follow=True)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.state != SubmissionStates.WITHDRAWN


@pytest.mark.django_db
def test_submission_withdraw_if_rejected(speaker_client, submission):
    with scope(event=submission.event):
        submission.reject()

    response = speaker_client.post(submission.urls.withdraw, follow=True)
    assert response.status_code == 200
    with scope(event=submission.event):
        submission.refresh_from_db()
        assert submission.state != SubmissionStates.WITHDRAWN

[((498, 1, 498, 63), 'pytest.mark.parametrize', 'pytest.mark.parametrize', ({(498, 25, 498, 47): '"""request_availability"""', (498, 49, 498, 62): '(True, False)'}, {}), "('request_availability', (True, False))", False, 'import pytest\n'), ((363, 8, 363, 59), 'django.core.files.uploadedfile.SimpleUploadedFile', 'SimpleUploadedFile', ({(363, 27, 363, 41): '"""testfile.txt"""', (363, 43, 363, 58): "b'file_content'"}, {}), "('testfile.txt', b'file_content')", False, 'from django.core.files.uploadedfile import SimpleUploadedFile\n'), ((86, 9, 86, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((91, 12, 91, 63), 'django.core.files.uploadedfile.SimpleUploadedFile', 'SimpleUploadedFile', ({(91, 31, 91, 45): '"""testfile.txt"""', (91, 47, 91, 62): "b'file_content'"}, {}), "('testfile.txt', b'file_content')", False, 'from django.core.files.uploadedfile import SimpleUploadedFile\n'), ((117, 9, 117, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((131, 9, 131, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((148, 9, 148, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((157, 9, 157, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((173, 9, 173, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((203, 9, 203, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((219, 9, 219, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((226, 9, 226, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((243, 9, 243, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((260, 9, 260, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((295, 9, 295, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((309, 9, 309, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((360, 9, 360, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((377, 9, 377, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((400, 9, 400, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((407, 9, 407, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((411, 9, 411, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((419, 9, 419, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((425, 9, 425, 27), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((570, 9, 570, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((575, 9, 575, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((583, 9, 583, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((589, 9, 589, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((596, 9, 596, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((601, 9, 601, 38), 'django_scopes.scope', 'scope', (), '', False, 'from django_scopes import scope\n'), ((439, 8, 439, 76), 'django.urls.reverse', 'reverse', (), '', False, 'from django.urls import reverse\n'), ((450, 8, 450, 76), 'django.urls.reverse', 'reverse', (), '', False, 'from django.urls import reverse\n'), ((269, 16, 269, 50), 'rest_framework.authtoken.models.Token.objects.filter', 'Token.objects.filter', (), '', False, 'from rest_framework.authtoken.models import Token\n'), ((278, 16, 278, 50), 'rest_framework.authtoken.models.Token.objects.filter', 'Token.objects.filter', (), '', False, 'from rest_framework.authtoken.models import Token\n')]