from typing import *
import pytest
from rich import print
from django.contrib.auth.models import User
from wcd_settings.registries import (
    UserSettingsDescriptor, AppSettingsDescriptor, Registry,
)
from wcd_settings.resolvers import AppSettingsResolver, UserSettingsResolver
from wcd_settings.models import AppSettings, UserSettings
from wcd_settings.setters import SettingsSetter, SettingsSetAction

from .fixtures import Simple


@pytest.mark.django_db
def test_app_save(django_assert_num_queries, registry: Registry, simple_app_settings: AppSettingsDescriptor):
    AppSettings.objects.create(
        key=simple_app_settings.key, config={"title": "test"},
    )

    with django_assert_num_queries(0):
        resolver = AppSettingsResolver(
            registry=registry, queryset=AppSettings.objects.all(),
        )
        setter = SettingsSetter(resolver, lambda key, dto: AppSettings(key=key))

    with django_assert_num_queries(1):
        entry: Optional[Simple] = resolver.get(simple_app_settings.key)

    assert entry is not None
    entry = entry.model_copy()
    entry.title = 'test2'

    with django_assert_num_queries(2):
        setter.save(simple_app_settings.key, entry)

    with django_assert_num_queries(0):
        entry2: Optional[Simple] = resolver.get(simple_app_settings.key)

    assert id(entry) != id(entry2)
    assert entry2.title == 'test2'

    resolver.clear()

    with django_assert_num_queries(1):
        entry: Optional[Simple] = resolver.get(simple_app_settings.key)

    assert isinstance(entry, Simple)
    assert entry.title == 'test2'


@pytest.mark.django_db
def test_user_save(
    django_assert_num_queries,
    registry: Registry,
    simple_user_settings: UserSettingsDescriptor,
):
    KEY = simple_user_settings.key
    user = User.objects.create_user(username='test', password='test')

    with django_assert_num_queries(0):
        resolver = UserSettingsResolver(
            registry=registry, queryset=UserSettings.objects.all(),
        )
        setter = SettingsSetter(
            resolver,
            lambda key, dto: UserSettings(key=key[1], user_id=key[0]),
        )

    entry_none: Optional[Simple] = resolver.get((user.pk, KEY))
    assert isinstance(entry_none, Simple)
    assert entry_none.title is None

    entry = Simple(title='test')
    setter.save((user.pk, KEY), entry)
    entry2: Optional[Simple] = resolver.get((user.pk, KEY))

    resolver.clear()

    with django_assert_num_queries(1):
        entry: Optional[Simple] = resolver.get((user.pk, KEY))

    assert entry is not None
    entry = entry.model_copy()
    entry.title = 'test2'

    with django_assert_num_queries(2):
        setter.save((user.pk, KEY), entry)

    with django_assert_num_queries(0):
        entry2: Optional[Simple] = resolver.get((user.pk, KEY))

    assert id(entry) != id(entry2)
    assert entry2.title == 'test2'

    resolver.clear()

    with django_assert_num_queries(1):
        entry: Optional[Simple] = resolver.get((user.pk, KEY))

    assert isinstance(entry, Simple)
    assert entry.title == 'test2'

    user2 = User.objects.create_user(username='test2', password='test')

    entry = entry.model_copy()
    entry.title = 'test3'

    with django_assert_num_queries(2) as q:
        setter.save((user2.pk, KEY), entry)

    resolver.clear()

    with django_assert_num_queries(1):
        uentry: Optional[Simple] = resolver.get((user.pk, KEY))

    assert uentry.title == 'test2'

    with django_assert_num_queries(1):
        u2entry: Optional[Simple] = resolver.get((user2.pk, KEY))

    assert u2entry.title == 'test3'

    resolver.clear()

    with django_assert_num_queries(1):
        resolver.prepare([
            (user.pk, KEY),
            (user2.pk, KEY),
        ])

    with django_assert_num_queries(0):
        uentry: Optional[Simple] = resolver.get((user.pk, KEY))

    assert uentry.title == 'test2'

    with django_assert_num_queries(0):
        u2entry: Optional[Simple] = resolver.get((user2.pk, KEY))

    assert u2entry.title == 'test3'


@pytest.mark.django_db
def test_user_save_partial(
    django_assert_num_queries,
    registry: Registry,
    simple_user_settings: UserSettingsDescriptor,
    other_simple_user_settings: UserSettingsDescriptor,
):
    KEY = simple_user_settings.key
    OTHER_KEY = other_simple_user_settings.key
    user = User.objects.create_user(username='test', password='test')
    resolver = UserSettingsResolver(
        registry=registry, queryset=UserSettings.objects.all(),
    )
    setter = SettingsSetter(
        resolver,
        lambda key, dto: UserSettings(key=key[1], user_id=key[0]),
    )

    commit, failures = setter.set([
        SettingsSetAction(key=(user.pk, KEY), path=['title'], value='text3'),
        SettingsSetAction(key=(user.pk, OTHER_KEY), path=['nested', 'value', 1], value='nested2'),
        SettingsSetAction(key=(user.pk, OTHER_KEY), path=['nested', 'value', 0], value='nested'),
    ])

    assert len(failures) == 0

    with django_assert_num_queries(1):
        commit()

    user_1: Simple = resolver.get((user.pk, KEY))
    assert user_1.title == 'text3'
    assert user_1.nested.value == []

    user_1: Simple = resolver.get((user.pk, OTHER_KEY))
    assert user_1.title == None
    assert user_1.nested.value == ['nested', 'nested2']

    resolver.clear()
