# Copyright 2017, Juniper Networks, Inc
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import logging

from django.template import defaultfilters as filters
from django.urls import reverse
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext_lazy

from horizon import exceptions
from horizon import tables
from openstack_dashboard.dashboards.project.networks.ports \
    import tables as port_tables
from openstack_dashboard import policy

from neutron_fwaas_dashboard.api import fwaas_v2 as api_fwaas_v2


LOG = logging.getLogger(__name__)


class AddRuleLink(tables.LinkAction):
    name = "addrule"
    verbose_name = _("Add Rule")
    url = "horizon:project:firewalls_v2:addrule"
    classes = ("ajax-modal",)
    icon = "plus"
    policy_rules = (("neutron-fwaas", "create_fwaas_firewall_rule"),)


class AddPolicyLink(tables.LinkAction):
    name = "addpolicy"
    verbose_name = _("Add Policy")
    url = "horizon:project:firewalls_v2:addpolicy"
    classes = ("ajax-modal", "btn-addpolicy",)
    icon = "plus"
    policy_rules = (("neutron-fwaas", "create_fwaas_firewall_policy"),)


class AddFirewallGroupLink(tables.LinkAction):
    name = "addfirewallgroup"
    verbose_name = _("Create Firewall Group")
    url = "horizon:project:firewalls_v2:addfirewallgroup"
    classes = ("ajax-modal",)
    icon = "plus"
    policy_rules = (("neutron-fwaas", "create_fwaas_firewall_group"),)


class DeleteRuleLink(policy.PolicyTargetMixin, tables.DeleteAction):
    name = "deleterule"
    policy_rules = (("neutron-fwaas", "delete_fwaas_firewall_rule"),)

    @staticmethod
    def action_present(count):
        return ungettext_lazy(
            u"Delete Rule",
            u"Delete Rules",
            count
        )

    @staticmethod
    def action_past(count):
        return ungettext_lazy(
            u"Scheduled deletion of Rule",
            u"Scheduled deletion of Rules",
            count
        )

    def allowed(self, request, datum=None):
        # TODO(Sarath Mekala): If the rule is associated with a policy then
        # return false.
        return True

    def delete(self, request, obj_id):
        try:
            api_fwaas_v2.rule_delete(request, obj_id)
        except Exception as e:
            exceptions.handle(request, _('Unable to delete rule. %s') % e)


class DeletePolicyLink(policy.PolicyTargetMixin, tables.DeleteAction):
    name = "deletepolicy"
    policy_rules = (("neutron-fwaas", "delete_fwaas_firewall_policy"),)

    @staticmethod
    def action_present(count):
        return ungettext_lazy(
            u"Delete Policy",
            u"Delete Policies",
            count
        )

    @staticmethod
    def action_past(count):
        return ungettext_lazy(
            u"Scheduled deletion of Policy",
            u"Scheduled deletion of Policies",
            count
        )

    def delete(self, request, obj_id):
        try:
            api_fwaas_v2.policy_delete(request, obj_id)
        except Exception as e:
            exceptions.handle(request, _('Unable to delete policy. %s') % e)


class DeleteFirewallGroupLink(policy.PolicyTargetMixin,
                              tables.DeleteAction):
    name = "deletefirewallgroup"
    policy_rules = (("neutron-fwaas", "delete_fwaas_firewall_group"),)

    @staticmethod
    def action_present(count):
        return ungettext_lazy(
            u"Delete Firewall Group",
            u"Delete Firewall Groups",
            count
        )

    @staticmethod
    def action_past(count):
        return ungettext_lazy(
            u"Scheduled deletion of Firewall Group",
            u"Scheduled deletion of Firewall Groups",
            count
        )

    def delete(self, request, obj_id):
        try:
            api_fwaas_v2.firewall_group_delete(request, obj_id)
        except Exception as e:
            exceptions.handle(request,
                              _('Unable to delete firewall group. %s') % e)


class UpdateRuleLink(policy.PolicyTargetMixin, tables.LinkAction):
    name = "updaterule"
    verbose_name = _("Edit Rule")
    classes = ("ajax-modal", "btn-update",)
    policy_rules = (("neutron-fwaas", "update_fwaas_firewall_rule"),)

    def get_link_url(self, rule):
        return reverse("horizon:project:firewalls_v2:updaterule",
                       kwargs={'rule_id': rule.id})


class UpdatePolicyLink(policy.PolicyTargetMixin, tables.LinkAction):
    name = "updatepolicy"
    verbose_name = _("Edit Policy")
    classes = ("ajax-modal", "btn-update",)
    policy_rules = (("neutron-fwaas", "update_fwaas_firewall_policy"),)

    def get_link_url(self, policy):
        return reverse("horizon:project:firewalls_v2:updatepolicy",
                       kwargs={'policy_id': policy.id})


class UpdateFirewallGroupLink(policy.PolicyTargetMixin, tables.LinkAction):
    name = "updatefirewall"
    verbose_name = _("Edit Firewall Group")
    classes = ("ajax-modal", "btn-update",)
    policy_rules = (("neutron-fwaas", "update_firewall"),)

    def get_link_url(self, firewallgroup):
        return reverse("horizon:project:firewalls_v2:updatefirewall",
                       kwargs={'firewall_id': firewallgroup.id})

    def allowed(self, request, firewallgroup):
        return firewallgroup.status not in (
            "PENDING_CREATE",
            "PENDING_UPDATE",
            "PENDING_DELETE")


class InsertRuleToPolicyLink(policy.PolicyTargetMixin,
                             tables.LinkAction):
    name = "insertrule"
    verbose_name = _("Insert Rule")
    classes = ("ajax-modal", "btn-update",)
    policy_rules = (("neutron-fwaas", "show_fwaas_firewall_policy"),
                    ("neutron-fwaas", "insert_rule_fwaas_firewall_policy"),)

    def get_link_url(self, policy):
        return reverse("horizon:project:firewalls_v2:insertrule",
                       kwargs={'policy_id': policy.id})


class RemoveRuleFromPolicyLink(policy.PolicyTargetMixin,
                               tables.LinkAction):
    name = "removerule"
    verbose_name = _("Remove Rule")
    classes = ("ajax-modal",)
    policy_rules = (("neutron-fwaas", "show_fwaas_firewall_policy"),
                    ("neutron-fwaas", "firewall_policy_remove_rule"),)
    action_type = "danger"

    def get_link_url(self, policy):
        return reverse("horizon:project:firewalls_v2:removerule",
                       kwargs={'policy_id': policy.id})

    def allowed(self, request, policy):
        return bool(policy.rules)


class AddPortToFirewallGroupLink(policy.PolicyTargetMixin,
                                 tables.LinkAction):
    name = "addport"
    verbose_name = _("Add Port")
    classes = ("ajax-modal", "btn-update",)
    policy_rules = (("neutron-fwaas", "update_fwaas_firewall_group"),)

    def get_link_url(self, firewallgroup):
        return reverse("horizon:project:firewalls_v2:addport",
                       kwargs={'firewallgroup_id': firewallgroup.id})

    def allowed(self, request, firewallgroup):
        return firewallgroup.status not in (
            "PENDING_CREATE",
            "PENDING_UPDATE",
            "PENDING_DELETE")


class RemovePortFromFirewallGroupLink(policy.PolicyTargetMixin,
                                      tables.LinkAction):
    name = "removeport"
    verbose_name = _("Remove Port")
    classes = ("ajax-modal", "btn-update",)
    policy_rules = (("neutron-fwaas", "update_fwaas_firewall_group"),)

    def get_link_url(self, firewallgroup):
        return reverse("horizon:project:firewalls_v2:removeport",
                       kwargs={'firewallgroup_id': firewallgroup.id})

    def allowed(self, request, firewallgroup):
        return firewallgroup.status not in (
            "PENDING_CREATE",
            "PENDING_UPDATE",
            "PENDING_DELETE")


def get_rules_name(datum):
    return ', '.join([rule.name_or_id for rule in datum.rules])


def get_ports_name(datum):
    return len(datum.ports)


def get_ports_link(datum):
    url = reverse("horizon:project:firewalls_v2:firewallgroupdetails",
                  args=(datum.id,))
    return '%s?tab=%s__%s' % (url, 'firewallgrouptabs', 'ports_tab')


def get_ingress_policy_link(datum):
    if datum.ingress_firewall_policy_id:
        return reverse('horizon:project:firewalls_v2:policydetails',
                       kwargs={'policy_id': datum.ingress_firewall_policy_id})


def get_egress_policy_link(datum):
    if datum.egress_firewall_policy_id:
        return reverse('horizon:project:firewalls_v2:policydetails',
                       kwargs={'policy_id': datum.egress_firewall_policy_id})


def get_ingress_policy_name(datum):
    if datum.ingress_firewall_policy_id:
        return datum.ingress_policy.name


def get_egress_policy_name(datum):
    if datum.egress_firewall_policy_id:
        return datum.egress_policy.name


class RulesTable(tables.DataTable):
    ACTION_DISPLAY_CHOICES = (
        ("Allow", pgettext_lazy("Action Name of a Firewall Rule", u"ALLOW")),
        ("Deny", pgettext_lazy("Action Name of a Firewall Rule", u"DENY")),
        ("Reject", pgettext_lazy("Action Name of a Firewall Rule", u"REJECT")),
    )
    name = tables.Column("name_or_id",
                         verbose_name=_("Name"),
                         link="horizon:project:firewalls_v2:ruledetails")
    description = tables.Column('description', verbose_name=_('Description'))
    ip_version = tables.Column('ip_version', verbose_name=('IP Version'))
    protocol = tables.Column("protocol",
                             filters=(lambda v: filters.default(v, _("ANY")),
                                      filters.upper,),
                             verbose_name=_("Protocol"))
    source_ip_address = tables.Column("source_ip_address",
                                      verbose_name=_("Source IP"))
    source_port = tables.Column("source_port",
                                verbose_name=_("Source Port"))
    destination_ip_address = tables.Column("destination_ip_address",
                                           verbose_name=_("Destination IP"))
    destination_port = tables.Column("destination_port",
                                     verbose_name=_("Destination Port"))
    action = tables.Column("action",
                           display_choices=ACTION_DISPLAY_CHOICES,
                           verbose_name=_("Action"))
    shared = tables.Column("shared",
                           verbose_name=_("Shared"),
                           filters=(filters.yesno, filters.capfirst))
    enabled = tables.Column("enabled",
                            verbose_name=_("Enabled"),
                            filters=(filters.yesno, filters.capfirst))

    class Meta(object):
        name = "rulestable"
        verbose_name = _("Rules")
        table_actions = (AddRuleLink,
                         DeleteRuleLink,
                         tables.NameFilterAction)
        row_actions = (UpdateRuleLink, DeleteRuleLink)


class PoliciesTable(tables.DataTable):
    name = tables.Column("name_or_id",
                         verbose_name=_("Name"),
                         link="horizon:project:firewalls_v2:policydetails")
    description = tables.Column('description', verbose_name=_('Description'))
    firewall_rules = tables.Column(get_rules_name,
                                   verbose_name=_("Rules"))
    shared = tables.Column("shared",
                           verbose_name=_("Shared"),
                           filters=(filters.yesno, filters.capfirst))
    audited = tables.Column("audited",
                            verbose_name=_("Audited"),
                            filters=(filters.yesno, filters.capfirst))

    class Meta(object):
        name = "policiestable"
        verbose_name = _("Policies")
        table_actions = (AddPolicyLink,
                         DeletePolicyLink,
                         tables.NameFilterAction)
        row_actions = (UpdatePolicyLink, InsertRuleToPolicyLink,
                       RemoveRuleFromPolicyLink, DeletePolicyLink)


class FirewallGroupsTable(tables.DataTable):
    STATUS_DISPLAY_CHOICES = (
        ("Active", pgettext_lazy("Current status of a Firewall Group",
                                 u"Active")),
        ("Down", pgettext_lazy("Current status of a Firewall Group",
                               u"Down")),
        ("Error", pgettext_lazy("Current status of a Firewall Group",
                                u"Error")),
        ("Created", pgettext_lazy("Current status of a Firewall Group",
                                  u"Created")),
        ("Pending_Create", pgettext_lazy("Current status of a Firewall Group",
                                         u"Pending Create")),
        ("Pending_Update", pgettext_lazy("Current status of a Firewall Group",
                                         u"Pending Update")),
        ("Pending_Delete", pgettext_lazy("Current status of a Firewall Group",
                                         u"Pending Delete")),
        ("Inactive", pgettext_lazy("Current status of a Firewall Group",
                                   u"Inactive")),
    )
    ADMIN_STATE_DISPLAY_CHOICES = (
        ("UP", pgettext_lazy("Admin state of a Firewall Group", u"UP")),
        ("DOWN", pgettext_lazy("Admin state of a Firewall Group", u"DOWN")),
    )

    name = tables.Column(
        "name_or_id",
        verbose_name=_("Name"),
        link="horizon:project:firewalls_v2:firewallgroupdetails")
    description = tables.Column('description', verbose_name=_('Description'))
    ingress_firewall_policy_id = tables.Column(
        get_ingress_policy_name,
        link=get_ingress_policy_link,
        verbose_name=_("Ingress Policy"))
    egress_firewall_policy_id = tables.Column(get_egress_policy_name,
                                              link=get_egress_policy_link,
                                              verbose_name=_("Egress Policy"))
    ports = tables.Column(get_ports_name,
                          link=get_ports_link,
                          verbose_name=_("Ports"))

    status = tables.Column("status",
                           verbose_name=_("Status"),
                           display_choices=STATUS_DISPLAY_CHOICES)
    admin_state_up = tables.Column("admin_state_up",
                                   verbose_name=_("Admin State"))
    shared = tables.Column("shared",
                           verbose_name=_("Shared"),
                           filters=(filters.yesno, filters.capfirst))

    class Meta(object):
        name = "FirewallGroupsTable"
        verbose_name = _("Firewall Groups")
        table_actions = (AddFirewallGroupLink,
                         DeleteFirewallGroupLink,
                         tables.NameFilterAction)
        row_actions = (
            UpdateFirewallGroupLink,
            DeleteFirewallGroupLink,
            AddPortToFirewallGroupLink,
            RemovePortFromFirewallGroupLink)


class FirewallGroupPortsTable(port_tables.PortsTable):

    class Meta(object):
        name = 'ports'
        verbose_name = _('Ports')
        table_actions = []
        row_actions = []
