# -*- coding: utf-8 -*-
import six
from django.contrib.auth.models import Group, Permission
from django.test import override_settings

from django_extensions.collision_resolvers import AppNameCR, AppsOrderCR, BaseCR, PathBasedCR
from tests.collisions.models import Group as Group_Col
from tests.collisions.models import Name as Name_Col
from tests.collisions.models import Note as Note_Col
from tests.collisions.models import SystemUser, UniqueModel
from tests.management.commands.shell_plus_tests.test_utils import AutomaticShellPlusImportsTestCase
from tests.testapp.models import Name, Note, UniqueTestAppModel
from tests.testapp.models import Permission as TAPermission


# Bad user defined collision resolvers:

class TestAppsOrderCR(AppsOrderCR):
    pass


class TestAppNameCR(AppNameCR):
    pass


def collision_resolver_which_is_not_class():
    pass


class CRNotExtendingFromBase:
    pass


class CRNoFunction(BaseCR):
    pass


class CRNoArguments(BaseCR):
    def resolve_collisions(self):
        pass


class CRBadResult(BaseCR):
    def resolve_collisions(self, namespace):
        return 1


class CRBadKey(BaseCR):
    def resolve_collisions(self, namespace):
        return {1: 'a'}


class CRBadValue(BaseCR):
    def resolve_collisions(self, namespace):
        return {'a': 1}


class CRBadTransformPath(PathBasedCR):
    def transform_import(self, module_path):
        return 1


class CRTestCase(AutomaticShellPlusImportsTestCase):
    def _assert_models_present_under_names(self, group, group_col, name, name_col, note, note_col, system_user,
                                           unique_model, permission, test_app_permission, unique_test_app_model):
        self.run_shell_plus()
        self.assert_imported_under_names(Group, group)
        self.assert_imported_under_names(Group_Col, group_col)
        self.assert_imported_under_names(Name, name)
        self.assert_imported_under_names(Name_Col, name_col)
        self.assert_imported_under_names(Note, note)
        self.assert_imported_under_names(Note_Col, note_col)
        self.assert_imported_under_names(SystemUser, system_user)
        self.assert_imported_under_names(UniqueModel, unique_model)
        self.assert_imported_under_names(Permission, permission)
        self.assert_imported_under_names(TAPermission, test_app_permission)
        self.assert_imported_under_names(UniqueTestAppModel, unique_test_app_model)

    def test_legacy_collision_resolver(self):
        self._assert_models_present_under_names(
            set(), {'Group'}, {'Name'}, set(), {'Note'}, set(), {'SystemUser'}, {'UniqueModel'}, set(), {'Permission'},
            {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_app_name_prefix_collision_resolver(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group'}, {'tests_collisions_Group', 'Group'}, {'django_extensions_Name', 'Name'},
            {'tests_collisions_Name'}, {'django_extensions_Note', 'Note'}, {'tests_collisions_Note'}, {'SystemUser'},
            {'UniqueModel'}, {'django_contrib_auth_Permission'}, {'tests_testapp_Permission', 'Permission'},
            {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNameSuffixCR',
    )
    def test_app_name_suffix_collision_resolver(self):
        self._assert_models_present_under_names(
            {'Group_django_contrib_auth'}, {'Group_tests_collisions', 'Group'}, {'Name_django_extensions', 'Name'},
            {'Name_tests_collisions'}, {'Note_django_extensions', 'Note'}, {'Note_tests_collisions'},
            {'SystemUser'}, {'UniqueModel'}, {'Permission_django_contrib_auth'},
            {'Permission_tests_testapp', 'Permission'}, {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppLabelPrefixCR',
    )
    def test_app_label_prefix_collision_resolver(self):
        self._assert_models_present_under_names(
            {'auth_Group'}, {'collisions_Group', 'Group'}, {'django_extensions_Name', 'Name'},
            {'collisions_Name'}, {'django_extensions_Note', 'Note'}, {'collisions_Note'}, {'SystemUser'},
            {'UniqueModel'}, {'auth_Permission'}, {'testapp_Permission', 'Permission'},
            {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppLabelSuffixCR',
    )
    def test_app_label_suffix_collision_resolver(self):
        self._assert_models_present_under_names(
            {'Group_auth'}, {'Group_collisions', 'Group'}, {'Name_django_extensions', 'Name'},
            {'Name_collisions'}, {'Note_django_extensions', 'Note'}, {'Note_collisions'},
            {'SystemUser'}, {'UniqueModel'}, {'Permission_auth'},
            {'Permission_testapp', 'Permission'}, {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.FullPathCR',
    )
    def test_full_path_collision_resolver(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group'}, {'tests_collisions_Group', 'Group'},
            {'tests_testapp_Name', 'Name'}, {'tests_collisions_Name'},
            {'tests_testapp_Note', 'Note'}, {'tests_collisions_Note'}, {'SystemUser'}, {'UniqueModel'},
            {'django_contrib_auth_Permission'},
            {'tests_testapp_Permission', 'Permission'}, {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.InstalledAppsOrderCR',
    )
    def test_installed_apps_order_collision_resolver(self):
        self._assert_models_present_under_names(
            {'Group'}, set(), set(), {'Name'}, set(), {'Note'}, {'SystemUser'}, {'UniqueModel'}, {'Permission'}, set(),
            {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.TestAppsOrderCR',
    )
    def test_installed_bad_order_collision_resolver(self):
        with six.assertRaisesRegex(self, AssertionError, "You must define APP_PRIORITIES in your resolver class!"):
            self._assert_models_present_under_names(
                set(), set(), set(), set(), set(), set(), set(), set(), set(), set(), set(),
            )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.TestAppNameCR',
    )
    def test_installed_apps_bad_name_collision_resolver(self):
        with six.assertRaisesRegex(self, AssertionError, "You must define MODIFICATION_STRING in your resolver class!"):
            self._assert_models_present_under_names(
                set(), set(), set(), set(), set(), set(), set(), set(), set(), set(), set(),
            )

    def _assert_bad_resolver(self, message):
        with six.assertRaisesRegex(self, AssertionError, message):
            self._assert_models_present_under_names(
                set(), set(), set(), set(), set(), set(), set(), set(), set(), set(), set(),
            )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.collision_resolver_which_is_not_class',
    )
    def test_installed_apps_not_class_collision_resolver(self):
        self._assert_bad_resolver("SHELL_PLUS_MODEL_IMPORTS_RESOLVER must be subclass of BaseCR!")

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRNotExtendingFromBase',
    )
    def test_installed_apps_not_subclass_of_base(self):
        self._assert_bad_resolver("SHELL_PLUS_MODEL_IMPORTS_RESOLVER must be subclass of BaseCR!")

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRNoFunction',
    )
    def test_installed_apps_no_resolve_conflicts_function(self):
        with six.assertRaisesRegex(
            self, TypeError, "Can't instantiate abstract class CRNoFunction with abstract methods resolve_collisions"
        ):
            self._assert_models_present_under_names(
                set(), set(), set(), set(), set(), set(), set(), set(), set(), set(), set(),
            )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRNoArguments',
    )
    def test_installed_apps_no_arguments_resolve_conflicts(self):
        self._assert_bad_resolver("resolve_collisions function must take one argument!")

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRBadResult',
    )
    def test_installed_apps_bad_result(self):
        self._assert_bad_resolver("Result of resolve_collisions function must be a dict!")

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRBadKey',
    )
    def test_installed_apps_bad_key(self):
        self._assert_bad_resolver("key in collision resolver result should be str not 1")

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRBadValue',
    )
    def test_installed_apps_bad_value(self):
        self._assert_bad_resolver("value in collision resolver result should be str not 1")

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='tests.management.commands.shell_plus_tests.test_collision_resolver.CRBadTransformPath'
    )
    def test_bad_transform_path(self):
        self._assert_bad_resolver("result of transform_import must be str!")

    @override_settings(
        SHELL_PLUS_MODEL_ALIASES={'testapp': {'Note': 'MyFunnyNote'}},
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_app_name_prefix_collision_resolver_with_model_alias(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group'}, {'tests_collisions_Group', 'Group'}, {'django_extensions_Name', 'Name'},
            {'tests_collisions_Name'}, {'MyFunnyNote'}, {'Note'}, {'SystemUser'},
            {'UniqueModel'}, {'django_contrib_auth_Permission'}, {'tests_testapp_Permission', 'Permission'},
            {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_ALIASES={'testapp': {'Note': 'Name'}},
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_app_name_prefix_collision_resolver_with_clash_because_of_model_alias(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group'}, {'tests_collisions_Group', 'Group'}, {'django_extensions_Name'},
            {'tests_collisions_Name'}, {'django_extensions_Note', 'Name'}, {'Note'}, {'SystemUser'},
            {'UniqueModel'}, {'django_contrib_auth_Permission'}, {'tests_testapp_Permission', 'Permission'},
            {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_APP_PREFIXES={'testapp': 'main_app'},
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_app_name_prefix_collision_resolver_with_app_prefixes(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group'}, {'tests_collisions_Group', 'Group'}, {'main_app_Name'},
            {'Name'}, {'main_app_Note'}, {'Note'}, {'SystemUser'},
            {'UniqueModel'}, {'Permission'}, {'main_app_Permission'}, {'main_app_UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_APP_PREFIXES={'testapp': 'main_app', 'collisions': 'main_app'},
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_app_name_prefix_collision_resolver_with_clashing_app_prefixes(self):
        self._assert_models_present_under_names(
            {'Group'}, {'main_app_Group'}, {'django_extensions_Name', 'main_app_Name'},
            {'tests_collisions_Name'}, {'django_extensions_Note', 'main_app_Note'},
            {'tests_collisions_Note'}, {'main_app_SystemUser'}, {'main_app_UniqueModel'}, {'Permission'},
            {'main_app_Permission'}, {'main_app_UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_APP_PREFIXES={'testapp': 'main_app', 'auth': 'main_app'},
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_app_name_prefix_collision_resolver_clash_with_3rd_party(self):
        self._assert_models_present_under_names(
            {'main_app_Group'}, {'Group'}, {'main_app_Name'},
            {'Name'}, {'main_app_Note'}, {'Note'}, {'SystemUser'},
            {'UniqueModel'}, {'django_contrib_auth_Permission'}, {'main_app_Permission', 'tests_testapp_Permission'},
            {'main_app_UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_APP_PREFIXES={'testapp': 'testapp', 'auth': 'auth', 'collisions': 'collisions'},
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCR',
    )
    def test_no_collisions_because_of_app_prefixes(self):
        self._assert_models_present_under_names(
            {'auth_Group'}, {'collisions_Group'}, {'testapp_Name'},
            {'collisions_Name'}, {'testapp_Note'}, {'collisions_Note'}, {'collisions_SystemUser'},
            {'collisions_UniqueModel'}, {'auth_Permission'}, {'testapp_Permission'},
            {'testapp_UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNamePrefixCustomOrderCR',
    )
    def test_app_name_prefix_custom_order_collision_resolver(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group', 'Group'}, {'tests_collisions_Group'}, {'django_extensions_Name'},
            {'tests_collisions_Name', 'Name'}, {'django_extensions_Note'}, {'tests_collisions_Note', 'Note'},
            {'SystemUser'}, {'UniqueModel'}, {'django_contrib_auth_Permission', 'Permission'},
            {'tests_testapp_Permission'}, {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.AppNameSuffixCustomOrderCR',
    )
    def test_app_name_suffix_custom_order_collision_resolver(self):
        self._assert_models_present_under_names(
            {'Group_django_contrib_auth', 'Group'}, {'Group_tests_collisions'}, {'Name_django_extensions'},
            {'Name_tests_collisions', 'Name'}, {'Note_django_extensions'}, {'Note_tests_collisions', 'Note'},
            {'SystemUser'}, {'UniqueModel'}, {'Permission_django_contrib_auth', 'Permission'},
            {'Permission_tests_testapp'}, {'UniqueTestAppModel'},
        )

    @override_settings(
        SHELL_PLUS_MODEL_IMPORTS_RESOLVER='django_extensions.collision_resolvers.FullPathCustomOrderCR',
    )
    def test_full_path_custom_order_collision_resolver(self):
        self._assert_models_present_under_names(
            {'django_contrib_auth_Group', 'Group'}, {'tests_collisions_Group'},
            {'tests_testapp_Name'}, {'tests_collisions_Name', 'Name'},
            {'tests_testapp_Note'}, {'tests_collisions_Note', 'Note'}, {'SystemUser'}, {'UniqueModel'},
            {'django_contrib_auth_Permission', 'Permission'},
            {'tests_testapp_Permission'}, {'UniqueTestAppModel'},
        )
