# import django
import logging

from django.core.exceptions import (
    ValidationError,
)
from django.db import models

# from rest_framework.reverse import reverse

from access.models.tenant import Tenant

# from core import exceptions as centurion_exceptions
# from core.mixin.history_save import SaveHistory



class TenancyManager(
    models.Manager
):
    """Multi-Tennant Object Manager

    This manager specifically caters for the multi-tenancy features of Centurion ERP.
    """

    def get_queryset(self):
        """ Fetch the data

        When the model contains the user data, the query is filtered to their
        and the globally defined Tenancy only.

        Returns:
            (queryset): **super user**: return unfiltered data.
            (queryset): **not super user**: return data from the stored unique organizations.
        """

        # user = None    # When CenturionUser in use

        # if hasattr(self.model, 'context'):

        #     user = self.model.context['user']


        # if user:

        #     tencies = user.get_tenancies(int_list = True)

        #     if len(tenancies) > 0 and not request.user.is_superuser:

        #         if hasattr(self.model, 'organization'):
        #             return super().get_queryset().select_related('organization').filter(
        #                 models.Q(organization__in = tenancies)
        #             )

        #         return super().get_queryset().select_related('organization').filter(
        #             models.Q(organization__in = tenancies)
        #         )

        request = None

        if hasattr(self.model, 'context'):

            request = self.model.context['request']

        if request is not None:

            tenancies: list(str()) = []

            if request.app_settings.global_organization:

                tenancies += [ request.app_settings.global_organization.id ]


            if request.user.is_authenticated:

                for team in request.tenancy._user_teams:

                    if team.organization.id in tenancies:
                        continue

                    tenancies += [ team.organization.id ]


                if len(tenancies) > 0 and not request.user.is_superuser:

                    if hasattr(self.model, 'organization'):
                        return super().get_queryset().select_related('organization').filter(
                            models.Q(organization__in = tenancies)
                        )

                    return super().get_queryset().select_related('organization').filter(
                        models.Q(organization__in = tenancies)
                    )

        return super().get_queryset().select_related('organization')


class TenancyObjectOld:


    kb_model_name: str = None
    """Model name to use for KB article linking
    
    This value is derived from `<model>._meta.model_name`. This value should
    only be used when there is model inheritence.
    """

    _log: logging.Logger = None
    
    def get_log(self):

        if self._log is None:

            self._log = logging.getLogger('centurion.' + self._meta.app_label)

        return self._log

    page_layout: list = None

    note_basename: str = None
    """URL BaseName for the notes endpoint.

    Don't specify the `app_namespace`, use property `app_namespace` above.
    """




    def get_page_layout(self):
        """ FEtch the page layout"""

        return self.page_layout



    def get_app_namespace(self) -> str:
        """Fetch the Application namespace if specified.

        Returns:
            str: Application namespace suffixed with colin `:`
            None: No application namespace found.
        """

        app_namespace = ''

        if self.app_namespace:

            app_namespace = self.app_namespace + ':'

        return str(app_namespace)



    def get_url_kwargs_notes(self) -> dict:
        """Fetch the URL kwargs for model notes

        Returns:
            dict: notes kwargs required for generating the URL with `reverse`
        """

        return {
            'model_id': self.id
        }



class TenancyAbstractModel(
    # TenancyObjectOld,
    # models.Model,
):
    """ Tenancy Model Abstract class.

    This class is for inclusion within **every** model within Centurion ERP.
    Provides the required fields, functions and methods for multi tennant objects.
    Unless otherwise stated, **no** object within this class may be overridden.

    Raises:
        ValidationError: User failed to supply organization
    """

    objects = TenancyManager()
    """ Multi-Tenanant Objects """

    class Meta:
        abstract = True


    def validatate_organization_exists(self):
        """Ensure that the user did provide an organization

        Raises:
            ValidationError: User failed to supply organization.
        """

        if not self:
            raise ValidationError(
                code = 'required',
                message = 'You must provide an organization'
            )


    id = models.AutoField(
        blank=False,
        help_text = 'ID of the item',
        primary_key=True,
        unique=True,
        verbose_name = 'ID'
    )

    organization = models.ForeignKey(
        Tenant,
        blank = False,
        help_text = 'Tenant this belongs to',
        null = False,
        on_delete = models.CASCADE,
        related_name = '+',
        validators = [
            validatate_organization_exists
        ],
        verbose_name = 'Tenant'
    )

    is_global = models.BooleanField(
        blank = False,
        default = False,
        help_text = 'Is this a global object?',
        null = False,
        verbose_name = 'Global Object'
    )

    model_notes = models.TextField(
        blank = True,
        default = None,                            # ToDo: Remove this field
        help_text = 'Tid bits of information',
        null = True,
        verbose_name = 'Notes',
    )



    def get_tenant(self) -> Tenant:
        """ Return the models Tenancy

        This model can be safely over-ridden as long as it returns the models
        tenancy
        """
        return self.organization
