class RoutingTable(HacsUtilsModel):
    """
    JSON Field Permitted Format/Python Data pattern
    -----------------------------------------------
    urls: [{'prefix': None, 'url_module': None, namespace=None, app_name: None}]
    OR [{'prefix': None, 'url_module': (module, app_name), namespace=None}]

    handlers: {'handler400': None, 'handler403': None, 'handler404': None, 'handler500': None}
    """
    description = models.TextField(_('description'), null=True, blank=True)
    urls = JSONField(_('URLs'), null=False, blank=False, validators=[UrlModulesValidator()])
    handlers = JSONField(_('Handlers'), null=True, blank=True, default='', validators=[HttpHandlerValidator()])
    generated_module = models.CharField(_('Generated Module'), null=True, blank=True, default=None, max_length=255)
    is_active = models.BooleanField(_('Is Active'), null=False, blank=True, default=True)
    is_deleted = models.BooleanField(_('Soft Delete'), null=False, blank=True, default=False)

    class Meta:
        db_table = 'hacs_routing_table'
        verbose_name = _('routing table')
        verbose_name_plural = _('routes table')

    def __str__(self):
        """
        """
        return self.name
        class NewsItem(HacsItemModel):

            extra_info = JSONField(null=True)

            class Meta:
                app_label = "hacs"
                db_table = "test_news_item"
        class DateFolder(HacsContainerModel):

            extra_info = JSONField(null=True)

            class Meta:
                app_label = "hacs"
                db_table = "test_date_folder"
class HacsWorkflowModel(HacsUtilsModel):
    """
    """

    # If you want to use custom role map for each permission. Other by default global map will be respected.
    # Data Format: {'permission name': ('role1', 'role2')}
    permissions_map = JSONField(null=True, blank=True)

    # Which object states will be handled by this workflow. Also will be used as validator while applied this workflow
    # on specified content type.
    states = JSONField(null=True, blank=True)

    # Targeted state after object is created
    default_state = models.CharField(
        max_length=64,
        null=False,
        blank=True,
        default=getattr(settings, 'HACS_DEFAULT_STATE', HACS_DEFAULT_STATE))

    # This the important for object guard. Security Manger actually lookup with this.
    # Validation Rule: if one state has no requested action by default will be rejected even if Manager User.
    # Data Format: {
    #   'state': {'action1': ('permission1', 'permission2'), 'action2': ('permission1', 'permission2')}
    # }
    states_permissions_map = JSONField(null=True, blank=True)

    # These are view/actions name that will change the state of an object. User should use this action instead of
    # direct change the state of object.
    # Transitions description could be another place, here only key is used.
    # Data Format: {'transition': {'target state': '', 'required permissions': () }}
    transitions = JSONField(null=True, blank=True)

    class Meta:
        app_label = "hacs"
        db_table = "hacs_workflows"
        verbose_name = _("workflow")
        verbose_name_plural = _("workflows")

    def __str__(self):
        """
        :return:
        """
        return "<%s: %s>" % (self.__class__.__name__, self.name)
class HacsContentType(HacsUtilsModel):
    """
    """
    objects = HacsContentTypeManager()

    # Validators: only HacsContainer & HacsContent type are allowed
    content_type = models.OneToOneField(
        'contenttypes.ContentType',
        on_delete=models.CASCADE,
        unique=True,
        validators=[]
    )
    # This could be applicable for Container or Item
    globally_allowed = models.BooleanField(blank=True, null=False)

    # Only applicable for Container Type Model
    allowed_content_types = models.ManyToManyField(
        'contenttypes.ContentType',
        blank=True,
        db_constraint=False,
        related_name="%(app_label)s_ctr_hctype_ctypes",
        related_query_name="%(app_label)s_ctr_hctype_ctypes_set"
    )
    # @TODO: need to decide workflow could be acquired. I think should not but respect parent permission (at least view)
    workflow = models.ForeignKey(
        "hacs.HacsWorkflowModel",
        db_constraint=False,
        on_delete=models.DO_NOTHING,
        null=True,
        blank=True,
        related_name="%(app_label)s_wfl_%(class)s_%(model_name)s_ctypes",
        related_query_name="%(app_label)s_wfl_%(class)s_ctypes_set"
    )
    # A dictionary map for content type permission
    # Data Format: {'action': (permission1. permission2)}
    # Usually should contain one permission 'object.create' map, when workflow is active
    # because other permissions will be ignored if workflow is available
    permissions_actions_map = JSONField(null=True, default={HACS_OBJECT_CREATE_ACTION: [HACS_CONTENT_ADD_PERMISSION, ]})

    class Meta:
        app_label = "hacs"
        db_table = "hacs_contenttypes"

    def natural_key(self):
        """"
         :return:
        """
        return self.content_type.natural_key()

    def __str__(self):
        """"""
        return self.content_type.__str__()
class ContentTypeRoutingRules(HacsUtilsModel):

    """
    """
    route = models.ForeignKey(RoutingTable,
                              on_delete=models.CASCADE,
                              db_column='route_id',
                              db_constraint=False,
                              related_name='hacs_route_contenttypes',
                              validators=[],
                              )
    site = models.ForeignKey(Site,
                             on_delete=models.CASCADE,
                             null=False,
                             blank=False,
                             related_name='hacs_site_contenttypes_at_routing_rules'
                            )
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, validators=[ContentTypeValidator()])
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    allowed_method = JSONField(_('Allowed Method'), null=True, blank=True)
    blacklisted_uri = models.CharField(
        _('blacklisted uri'),
        max_length=255,
        null=True,
        blank=True,
        help_text=_('regex formatted uri those will be treated as blacklisted and request will be '
                    'discarded by firewall'))
    whitelisted_uri = models.CharField(
        _('whitelisted uri'),
        max_length=255,
        null=True,
         blank=True,
         help_text='regex formatted uri those will be treated as whitelisted and request will '
                   'be discarded by firewall if uri not match')
    is_active = models.BooleanField(_('Is Active'), null=False, blank=True, default=True)
    objects = ContentTypeRoutingRulesManager()

    class Meta:
        db_table = 'hacs_ct_routing_rules'
        verbose_name = _('content type routing rules')
        verbose_name_plural = _('content types routing rules')
        unique_together = (("site", "content_type", "object_id"),)

    def natural_key(self):
        """
        :return:
        """
        try:
            site_natural_key = self.site.natural_key()
        except AttributeError:
            #  Bellow Django 1.10 `natural_key` is not implemented by django, but would be good if they do
            site_natural_key = self.site.pk
        return (site_natural_key, self.content_type.natural_key(), self.object_id, )

    natural_key.dependencies = ["hacs.RoutingTable", "sites.Site", "contenttypes.ContentType"]

    def __str__(self):
        """
        :return:
        """
        return "%s:%s:%s's routing rules" % (self.site.domain, self.content_type.app_label + "." +
                                              self.content_type.model, self.object_id)
class SiteRoutingRules(HacsUtilsModel):

    """
    """
    route = models.ForeignKey(RoutingTable,
                              on_delete=models.CASCADE,
                              db_column='route_id',
                              db_constraint=False,
                              related_name='hacs_route_sites')
    site = models.OneToOneField(Site,
                             on_delete=models.CASCADE,
                             unique=True,
                             null=False,
                             blank=False,
                             related_name='hacs_site_routes')

    allowed_method = JSONField(_('Allowed Method'), null=True, blank=True)
    blacklisted_uri = models.CharField(
        _('blacklisted uri'),
        max_length=255,
        null=True,
        blank=True,
        help_text=_('regex formatted uri those will be treated as blacklisted and request will be discarded by firewall'))

    whitelisted_uri = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        help_text=_('regex formatted uri those will be treated as whitelisted and request will '
                   'be discarded by firewall if uri not match'))

    is_active = models.BooleanField(_('Is Active'), null=False, blank=True, default=True)
    maintenance_mode = models.BooleanField(
        _('Maintenance Mode'),
        null=False,
        blank=True,
        default=False,
        help_text=_('Firewall will only response maintenance view and prevent any further execution '
                    'for all request if it is on'))

    objects = SiteRoutingRulesManager()

    class Meta:
        db_table = 'hacs_sites_routing_rules'
        verbose_name = _('site routing rules')
        verbose_name_plural = _('sites routing rules')

    def natural_key(self):
        """
        :return:
        """
        try:
            site_natural_key = self.site.natural_key()
        except AttributeError:
            # Right now `natural_key` is not implemented by django, but would be good if they do
            site_natural_key = self.site.pk
        return (site_natural_key, )

    natural_key.dependencies = ["hacs.RoutingTable", "sites.Site"]

    def __str__(self):
        """"""
        return "%s's routing rules" % self.site.domain