from typing import *
import pytest
import logging
from django.contrib.auth.models import User
from wcd_settings.registries import (
    SettingsDTO, UserSettingsDescriptor, AppSettingsDescriptor, Registry,
)
from wcd_settings.resolvers import AppSettingsResolver, UserSettingsResolver
from wcd_settings.models import AppSettings, UserSettings


@pytest.mark.django_db
def test_registration(caplog):

    registry = Registry()

    class Simple(SettingsDTO):
        title: str

    with caplog.at_level(logging.WARNING):
        registry.register(AppSettingsDescriptor(
            key='wcd_settings:test:simple',
            dto=Simple,
            schema=Simple.model_json_schema(),
        ))

    assert 'Model for wcd_settings:test:simple can\'t be initialized empty.' in caplog.text

    caplog.clear()


    class Simple2(SettingsDTO):
        title: Optional[str] = None


    with caplog.at_level(logging.WARNING):
        registry.register(AppSettingsDescriptor(
            key='wcd_settings:test:simple2',
            dto=Simple2,
            schema=Simple2.model_json_schema(),
        ))

    assert caplog.text == ''


@pytest.mark.django_db
def test_app_resolver_empty():
    registry = Registry()

    class Simple(SettingsDTO):
        title: Optional[str] = None

    registry.register(AppSettingsDescriptor(
        key='wcd_settings:test:simple',
        dto=Simple,
        schema=Simple.model_json_schema(),
    ))

    resolver = AppSettingsResolver(
        registry=registry, queryset=AppSettings.objects.all(),
    )
    entry: Optional[Simple] = resolver.get('wcd_settings:test:simple')

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


@pytest.mark.django_db
def test_user_resolver_empty():
    registry = Registry()

    class Simple(SettingsDTO):
        title: Optional[str] = None

    registry.register(UserSettingsDescriptor(
        key='wcd_settings:test:simple',
        dto=Simple,
        schema=Simple.model_json_schema(),
    ))

    resolver = UserSettingsResolver(
        registry=registry, queryset=UserSettings.objects.all(),
    )
    entry: Optional[Simple] = resolver.get((1, 'wcd_settings:test:simple'))

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


@pytest.mark.django_db
def test_user_resolver_with_partial_config():
    registry = Registry()

    class Complex(SettingsDTO):
        title: Optional[str] = None
        enabled: bool = False

    user = User.objects.create_user(username='partial', password='partial')
    UserSettings.objects.create(
        key='wcd_settings:test:complex', config={"title": 'bobako'},
        user_id=user.pk,
    )

    registry.register(UserSettingsDescriptor(
        key='wcd_settings:test:complex',
        dto=Complex,
        schema=Complex.model_json_schema(),
    ))

    resolver = UserSettingsResolver(
        registry=registry, queryset=UserSettings.objects.all(),
    )
    entry: Optional[Complex] = resolver.get((user.pk, 'wcd_settings:test:complex'))

    assert entry is not None
    assert isinstance(entry, Complex)
    assert entry.title == 'bobako'
    assert entry.enabled is False


@pytest.mark.django_db
def test_app_resolver_with_default_values():
    registry = Registry()

    class Defaults(SettingsDTO):
        count: int = 5
        name: str = "default"

    registry.register(AppSettingsDescriptor(
        key='wcd_settings:test:defaults',
        dto=Defaults,
        schema=Defaults.model_json_schema(),
    ))

    resolver = AppSettingsResolver(
        registry=registry, queryset=AppSettings.objects.all(),
    )
    entry: Optional[Defaults] = resolver.get('wcd_settings:test:defaults')

    assert entry is not None
    assert isinstance(entry, Defaults)
    assert entry.count == 5
    assert entry.name == "default"


@pytest.mark.django_db
def test_user_resolver_multiple_users():
    registry = Registry()

    class Simple(SettingsDTO):
        title: Optional[str] = None

    user1 = User.objects.create_user(username='user1', password='user1')
    user2 = User.objects.create_user(username='user2', password='user2')
    UserSettings.objects.create(
        key='wcd_settings:test:simple', config={"title": "user1title"},
        user_id=user1.pk,
    )
    UserSettings.objects.create(
        key='wcd_settings:test:simple', config={"title": "user2title"},
        user_id=user2.pk,
    )

    registry.register(UserSettingsDescriptor(
        key='wcd_settings:test:simple',
        dto=Simple,
        schema=Simple.model_json_schema(),
    ))

    resolver = UserSettingsResolver(
        registry=registry, queryset=UserSettings.objects.all(),
    )
    entry1: Optional[Simple] = resolver.get((user1.pk, 'wcd_settings:test:simple'))
    entry2: Optional[Simple] = resolver.get((user2.pk, 'wcd_settings:test:simple'))

    assert entry1.title == "user1title"
    assert entry2.title == "user2title"


@pytest.mark.django_db
def test_user_resolver():
    registry = Registry()

    class Simple(SettingsDTO):
        title: Optional[str] = None


    user = User.objects.create_user(username='test', password='test')
    UserSettings.objects.create(
        key='wcd_settings:test:simple', config={"title": "test"},
        user_id=user.pk,
    )

    registry.register(UserSettingsDescriptor(
        key='wcd_settings:test:simple',
        dto=Simple,
        schema=Simple.model_json_schema(),
    ))

    resolver = UserSettingsResolver(
        registry=registry, queryset=UserSettings.objects.all(),
    )
    entry: Optional[Simple] = resolver.get((user.pk, 'wcd_settings:test:simple'))

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

    false_entry: Optional[Simple] = resolver.get((user.pk + 1, 'wcd_settings:test:simple'))

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


@pytest.mark.django_db
def test_app_resolver(django_assert_num_queries):
    registry = Registry()

    class Simple(SettingsDTO):
        title: Optional[str] = None

    AppSettings.objects.create(
        key='wcd_settings:test:simple', config={"title": "test"},
    )

    registry.register(AppSettingsDescriptor(
        key='wcd_settings:test:simple',
        dto=Simple,
        schema=Simple.model_json_schema(),
    ))

    resolver = AppSettingsResolver(
        registry=registry, queryset=AppSettings.objects.all(),
    )

    with django_assert_num_queries(1):
        entry: Optional[Simple] = resolver.get('wcd_settings:test:simple')

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

    with django_assert_num_queries(0):
        entry: Optional[Simple] = resolver.get('wcd_settings:test:simple')

    assert entry.title == 'test'
