from typing import *
from rich import print
from django.urls import reverse
import pytest
from unittest import mock

from django.contrib.auth.models import User
from django.test import Client
from wcd_settings.registries import (
    UserSettingsDescriptor, AppSettingsDescriptor, Registry,
)
from wcd_settings.resolvers import (
    AppSettingsResolver, UserSettingsResolver,
)
from wcd_settings.models import AppSettings, UserSettings

from .fixtures import Simple


@pytest.mark.django_db
def test_app_set_view(
    rf,
    client: Client,
    registry: Registry,
    simple_app_settings: AppSettingsDescriptor,
):
    with mock.patch('wcd_settings.resolvers.app_settings_registry', registry):
        user = User.objects.create_user(username='test', password='test', is_superuser=True)
        client.force_login(user)

        response = client.post(
            reverse('wcd-settings-api-v1:app:set'),
            content_type='application/json',
            data={'actions': []},
        ).json()
        assert 'errors' in response
        assert len(response['errors']) == 1
        assert response['errors'][0]['reason'] == 'too_short'
        assert response['errors'][0]['path'] == []

        response = client.post(
            reverse('wcd-settings-api-v1:app:set'),
            content_type='application/json',
            data={'actions': [
                {'path': [simple_app_settings.key, 'title'], 'value': 'test'},
                {'path': [simple_app_settings.key, 'nested', 'value', 0], 'value': 'some'},
            ]},
        ).json()

        assert response['updated'] == 1

        resolver = AppSettingsResolver(
            registry=registry, queryset=AppSettings.objects.all(),
        )
        entry: Optional[Simple] = resolver.get(simple_app_settings.key)

        assert entry is not None
        assert isinstance(entry, Simple)
        assert entry.title == 'test'
        assert entry.nested.value == ['some']


@pytest.mark.django_db
def test_app_set_view_forbidden(
    rf,
    client: Client,
    registry: Registry,
    simple_app_settings: AppSettingsDescriptor,
):
    with mock.patch('wcd_settings.resolvers.app_settings_registry', registry):
        user = User.objects.create_user(username='test', password='test')
        client.force_login(user)

        response = client.post(
            reverse('wcd-settings-api-v1:app:set'),
            content_type='application/json',
            data={'actions': []},
        ).json()
        assert 'errors' in response
        assert len(response['errors']) == 1
        assert response['errors'][0]['reason'] == 'permission_denied'
        assert response['errors'][0]['path'] == []


@pytest.mark.django_db
def test_user_set_view(
    rf,
    client: Client,
    registry: Registry,
    simple_user_settings: UserSettingsDescriptor,
):
    with mock.patch('wcd_settings.resolvers.user_settings_registry', registry):
        user = User.objects.create_user(username='test', password='test')
        client.force_login(user)

        response = client.post(
            reverse('wcd-settings-api-v1:own:set'),
            content_type='application/json',
            data={'actions': []},
        ).json()
        assert 'errors' in response
        assert len(response['errors']) == 1
        assert response['errors'][0]['reason'] == 'too_short'
        assert response['errors'][0]['path'] == []

        response = client.post(
            reverse('wcd-settings-api-v1:own:set'),
            content_type='application/json',
            data={'actions': [
                {'path': [simple_user_settings.key, 'title'], 'value': 'test'},
                {'path': [simple_user_settings.key, 'nested', 'value', 0], 'value': 'some'},
                {'path': [simple_user_settings.key, 'any', 'depth', 0], 'value': 'some'},
            ]},
        ).json()

        assert response['updated'] == 1

        resolver = UserSettingsResolver(
            registry=registry, queryset=UserSettings.objects.all(),
        )
        entry: Optional[Simple] = resolver.get((user.pk, simple_user_settings.key))

        assert entry is not None
        assert isinstance(entry, Simple)
        assert entry.title == 'test'
        assert entry.nested.value == ['some']
        assert entry.any['depth'] == ['some']


@pytest.mark.django_db
def test_user_set_view_forbidden(
    rf,
    client: Client,
    registry: Registry,
    simple_user_settings: UserSettingsDescriptor,
):
    with mock.patch('wcd_settings.resolvers.user_settings_registry', registry):
        response = client.post(
            reverse('wcd-settings-api-v1:own:set'),
            content_type='application/json',
            data={'actions': []},
        ).json()
        assert 'errors' in response
        assert len(response['errors']) == 1
        assert response['errors'][0]['reason'] == 'permission_denied'
        assert response['errors'][0]['path'] == []


@pytest.mark.django_db
def test_user_set_view_inappropriate_value(
    rf,
    client: Client,
    registry: Registry,
    simple_user_settings: UserSettingsDescriptor,
):
    with mock.patch('wcd_settings.resolvers.user_settings_registry', registry):
        user = User.objects.create_user(username='test', password='test')
        client.force_login(user)

        response = client.post(
            reverse('wcd-settings-api-v1:own:set'),
            content_type='application/json',
            data={'actions': []},
        ).json()
        assert 'errors' in response
        assert len(response['errors']) == 1
        assert response['errors'][0]['reason'] == 'too_short'
        assert response['errors'][0]['path'] == []

        response = client.post(
            reverse('wcd-settings-api-v1:own:set'),
            content_type='application/json',
            data={'actions': [
                {'path': [simple_user_settings.key, 'title'], 'value': 100},
                {'path': [simple_user_settings.key, 'nested', 'value'], 'value': 100},
            ]},
        ).json()

        assert 'errors' in response
        errors = response['errors']
        assert len(errors) == 2
        title_error = next((
            x for x in errors
            if x['path'] == [simple_user_settings.key, 'title']
        ), None)
        assert title_error is not None
        assert title_error['reason'] == 'string_type'
        nested_error = next((
            x for x in errors
            if x['path'] == [simple_user_settings.key, 'nested', 'value']
        ), None)
        assert nested_error is not None
        assert nested_error['reason'] == 'list_type'

        resolver = UserSettingsResolver(
            registry=registry, queryset=UserSettings.objects.all(),
        )
        entry: Optional[Simple] = resolver.get((user.pk, simple_user_settings.key))

        assert entry is not None
        assert isinstance(entry, Simple)
        assert entry.title is None
