# -*- 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 query():
    return {
        'url': 'https://www.ziomek.dog/',
    }


@pytest.mark.django_db
def test_ok(authenticated_client: Client,
            query,
            account,
            mock_saves_process_save_task_apply_async: mock.Mock,
            ):
    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

    # 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=query['url'],
        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,
                          query,
                          account,
                          mock_saves_process_save_task_apply_async: mock.Mock,
                          ):
    # Given
    query['url'] = 'https://youtube.com/'

    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

    # 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=query['url'],
        is_netloc_banned=True,
    )


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

    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

    # 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,
                              query,
                              save_out,
                              account,
                              association_out,
                              mock_saves_process_save_task_apply_async: mock.Mock,
                              ):
    # Given
    query['url'] = save_out.url

    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

    # 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,
                                     query,
                                     other_account_save_out,
                                     account,
                                     mock_saves_process_save_task_apply_async: mock.Mock,
                                     ):
    # Given
    query['url'] = other_account_save_out.url

    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

    # 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,
                                               query,
                                               processed_save_out,
                                               account,
                                               mock_saves_process_save_task_apply_async: mock.Mock,
                                               ):
    # Given
    query['url'] = processed_save_out.url

    # When
    _ = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

    # Then
    mock_saves_process_save_task_apply_async.assert_not_called()


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

    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=effective_query,
    )

    # 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_query = {}

    # When
    result = authenticated_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=effective_query,
    )

    # 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,
                          query,
                          ):
    # When
    result = inactive_account_client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

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


@pytest.mark.django_db
def test_anonymous(client: Client,
                   query,
                   ):
    # When
    result = client.get(
        reverse('ui.integrations.ios.shortcut'),
        data=query,
    )

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