# -*- coding: utf-8 -*-
# type: ignore
from __future__ import annotations

import http
from unittest import mock
import uuid

from django.test import Client
from django.urls import reverse
import pytest
from pytest_django import asserts
import pytest_mock

from hotpocket_backend_testing.services.saves import (
    AssociationsTestingService,
    SaveProcessorTestingService,
    SavesTestingService,
)


@pytest.fixture
def mock_saves_process_save_task_apply_async(mocker: pytest_mock.MockerFixture,
                                             async_result,
                                             ) -> mock.Mock:
    return SaveProcessorTestingService().mock_process_save_task_apply_async(
        mocker=mocker, async_result=async_result,
    )


@pytest.fixture
def url_to_save():
    return 'https://www.ziomek.dog/'


@pytest.fixture
def payload_with_text(url_to_save):
    return {
        'text': url_to_save,
    }


@pytest.fixture
def payload_with_url(url_to_save):
    return {
        'url': url_to_save,
    }


@pytest.mark.parametrize(
    'payload_fixture_name',
    [
        'payload_with_text',
        'payload_with_url',
    ],
)
@pytest.mark.django_db
def test_ok(payload_fixture_name,
            request: pytest.FixtureRequest,
            authenticated_client: Client,
            account,
            url_to_save,
            mock_saves_process_save_task_apply_async: mock.Mock,
            ):
    # Given
    payload = request.getfixturevalue(payload_fixture_name)

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload,
    )

    # Then
    assert 'X-HotPocket-Testing-Save-PK' in result.headers
    assert 'X-HotPocket-Testing-Association-PK' in result.headers

    save_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Save-PK'])
    association_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Association-PK'])

    asserts.assertRedirects(
        result,
        reverse('ui.associations.post_save', args=(association_pk,)),
        fetch_redirect_response=False,
    )

    AssociationsTestingService().assert_created(
        pk=association_pk,
        account_uuid=account.pk,
        target_uuid=save_pk,
    )

    SavesTestingService().assert_created(
        pk=save_pk,
        account_uuid=account.pk,
        url=url_to_save,
        is_netloc_banned=False,
    )

    mock_saves_process_save_task_apply_async.assert_called_once_with(
        kwargs={
            'pk': save_pk,
        },
    )


@pytest.mark.django_db
def test_ok_netloc_banned(authenticated_client: Client,
                          payload_with_text,
                          account,
                          mock_saves_process_save_task_apply_async: mock.Mock,
                          ):
    # Given
    payload_with_text['text'] = 'https://youtube.com/'

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    save_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Save-PK'])
    association_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Association-PK'])

    asserts.assertRedirects(
        result,
        reverse('ui.associations.post_save', args=(association_pk,)),
        fetch_redirect_response=False,
    )

    SavesTestingService().assert_created(
        pk=save_pk,
        account_uuid=account.pk,
        url=payload_with_text['text'],
        is_netloc_banned=True,
    )


@pytest.mark.django_db
def test_ok_reuse_save(authenticated_client: Client,
                       payload_with_text,
                       save_out,
                       account,
                       mock_saves_process_save_task_apply_async: mock.Mock,
                       ):
    # Given
    payload_with_text['text'] = save_out.url

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    assert 'X-HotPocket-Testing-Save-PK' in result.headers
    assert 'X-HotPocket-Testing-Association-PK' in result.headers

    save_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Save-PK'])
    association_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Association-PK'])

    asserts.assertRedirects(
        result,
        reverse('ui.associations.post_save', args=(association_pk,)),
        fetch_redirect_response=False,
    )

    AssociationsTestingService().assert_created(
        pk=association_pk,
        account_uuid=account.pk,
        target_uuid=save_pk,
    )

    SavesTestingService().assert_reused(
        pk=save_pk,
        reference=save_out,
    )


@pytest.mark.django_db
def test_ok_reuse_association(authenticated_client: Client,
                              payload_with_text,
                              save_out,
                              account,
                              association_out,
                              mock_saves_process_save_task_apply_async: mock.Mock,
                              ):
    # Given
    payload_with_text['text'] = save_out.url

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    association_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Association-PK'])

    AssociationsTestingService().assert_reused(
        pk=association_pk,
        reference=association_out,
    )


@pytest.mark.django_db
def test_ok_reuse_other_account_save(authenticated_client: Client,
                                     payload_with_text,
                                     other_account_save_out,
                                     account,
                                     mock_saves_process_save_task_apply_async: mock.Mock,
                                     ):
    # Given
    payload_with_text['text'] = other_account_save_out.url

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    save_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Save-PK'])
    association_pk = uuid.UUID(result.headers['X-HotPocket-Testing-Association-PK'])

    AssociationsTestingService().assert_created(
        pk=association_pk,
        account_uuid=account.pk,
        target_uuid=save_pk,
    )

    SavesTestingService().assert_reused(
        pk=save_pk,
        reference=other_account_save_out,
    )


@pytest.mark.django_db
def test_ok_dont_process_reused_processed_save(authenticated_client: Client,
                                               payload_with_text,
                                               processed_save_out,
                                               account,
                                               mock_saves_process_save_task_apply_async: mock.Mock,
                                               ):
    # Given
    payload_with_text['text'] = processed_save_out.url

    # When
    _ = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    mock_saves_process_save_task_apply_async.assert_not_called()


@pytest.mark.django_db
def test_invalid_all_empty(authenticated_client: Client,
                           payload_with_text,
                           mock_saves_process_save_task_apply_async: mock.Mock,
                           ):
    # Given
    effective_payload = {
        key: ''
        for key
        in payload_with_text.keys()
    }

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=effective_payload,
    )

    # Then
    assert result.status_code == http.HTTPStatus.OK
    assert 'X-HotPocket-Testing-Save-PK' not in result.headers
    assert 'X-HotPocket-Testing-Association-PK' not in result.headers

    mock_saves_process_save_task_apply_async.assert_not_called()

    asserts.assertTemplateUsed(result, 'ui/errors/internal_server_error.html')


@pytest.mark.django_db
def test_invalid_all_missing(authenticated_client: Client,
                             mock_saves_process_save_task_apply_async: mock.Mock,
                             ):
    # Given
    effective_payload = {}

    # When
    result = authenticated_client.post(
        reverse('ui.integrations.pwa.share_sheet'),
        data=effective_payload,
    )

    # Then
    assert result.status_code == http.HTTPStatus.OK
    assert 'X-HotPocket-Testing-Save-PK' not in result.headers
    assert 'X-HotPocket-Testing-Association-PK' not in result.headers

    mock_saves_process_save_task_apply_async.assert_not_called()

    asserts.assertTemplateUsed(result, 'ui/errors/internal_server_error.html')


@pytest.mark.django_db
def test_inactive_account(inactive_account_client: Client,
                          payload_with_text,
                          ):
    # When
    result = inactive_account_client.get(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    asserts.assertRedirects(
        result,
        reverse(
            'ui.accounts.login',
            query=[
                (
                    'next',
                    reverse(
                        'ui.integrations.pwa.share_sheet',
                        query=payload_with_text,
                    ),
                ),
            ],
        ),
        fetch_redirect_response=False,
    )


@pytest.mark.django_db
def test_anonymous(client: Client,
                   payload_with_text,
                   ):
    # When
    result = client.get(
        reverse('ui.integrations.pwa.share_sheet'),
        data=payload_with_text,
    )

    # Then
    asserts.assertRedirects(
        result,
        reverse(
            'ui.accounts.login',
            query=[
                (
                    'next',
                    reverse(
                        'ui.integrations.pwa.share_sheet',
                        query=payload_with_text,
                    ),
                ),
            ],
        ),
        fetch_redirect_response=False,
    )
