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, 'pytest.mark.parametrize', 'pytest.mark.parametrize', False, 'import pytest\n'), (363, 'django.core.files.uploadedfile.SimpleUploadedFile', 'SimpleUploadedFile', False, 'from django.core.files.uploadedfile import SimpleUploadedFile\n'), (86, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (91, 'django.core.files.uploadedfile.SimpleUploadedFile', 'SimpleUploadedFile', False, 'from django.core.files.uploadedfile import SimpleUploadedFile\n'), (117, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (131, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (148, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (157, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (173, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (203, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (219, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (226, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (243, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (260, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (295, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (309, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (360, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (377, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (400, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (407, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (411, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (419, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (425, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (570, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (575, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (583, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (589, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (596, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (601, 'django_scopes.scope', 'scope', False, 'from django_scopes import scope\n'), (439, 'django.urls.reverse', 'reverse', False, 'from django.urls import reverse\n'), (450, 'django.urls.reverse', 'reverse', False, 'from django.urls import reverse\n'), (269, 'rest_framework.authtoken.models.Token.objects.filter', 'Token.objects.filter', False, 'from rest_framework.authtoken.models import Token\n'), (278, 'rest_framework.authtoken.models.Token.objects.filter', 'Token.objects.filter', False, 'from rest_framework.authtoken.models import Token\n')]