from __future__ import annotations

from django.conf import settings
from django.core.validators import URLValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_countries.fields import CountryField
from django_handleref.models import HandleRefModel
from django_inet.models import ASNField, IPAddressField, IPPrefixField, MacAddressField

from django_peeringdb import const
from django_peeringdb.fields import MultipleChoiceField


class LG_URLField(models.URLField):
    default_validators = [URLValidator(schemes=["http", "https", "telnet", "ssh"])]

    def __init__(self, *args, **kwargs):
        kwargs["max_length"] = 255
        super().__init__(*args, **kwargs)


class URLField(models.URLField):
    default_validators = [URLValidator(schemes=["http", "https"])]

    def __init__(self, *args, **kwargs):
        kwargs["max_length"] = 255
        super().__init__(*args, **kwargs)


class AddressModel(models.Model):
    """
    Postal Address
    """

    address1: models.CharField = models.CharField(
        _("Address 1"), max_length=255, blank=True
    )
    address2: models.CharField = models.CharField(
        _("Address 2"), max_length=255, blank=True
    )
    city: models.CharField = models.CharField(_("City"), max_length=255, blank=True)
    state: models.CharField = models.CharField(_("State"), max_length=255, blank=True)
    zipcode: models.CharField = models.CharField(
        _("Zip-Code"), max_length=48, blank=True
    )
    country: CountryField = CountryField(_("Country"), blank=True)

    suite: models.CharField = models.CharField(_("Suite"), max_length=255, blank=True)
    floor: models.CharField = models.CharField(_("Floor"), max_length=255, blank=True)

    latitude: models.DecimalField = models.DecimalField(
        _("Latitude"), max_digits=9, decimal_places=6, blank=True, null=True
    )
    longitude: models.DecimalField = models.DecimalField(
        _("Longitude"), max_digits=9, decimal_places=6, blank=True, null=True
    )

    class Meta:
        abstract = True

    def __str__(self):
        return self.address1


class OrganizationBase(HandleRefModel, AddressModel):
    name: models.CharField = models.CharField(_("Name"), max_length=255, unique=True)

    aka: models.CharField = models.CharField(
        _("Also Known As"), max_length=255, blank=True
    )
    name_long: models.CharField = models.CharField(
        _("Long Name"), max_length=255, blank=True
    )

    website: URLField = URLField(_("Website"), blank=True, default="")
    social_media = models.JSONField(_("Social Media"), default=dict, blank=True)
    notes: models.TextField = models.TextField(_("Notes"), blank=True)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}organization"
        verbose_name = _("Organization")
        verbose_name_plural = _("Organizations")

    class HandleRef:
        tag = "org"
        delete_cascade = ["net_set", "fac_set", "ix_set"]

    def __str__(self):
        return self.name


class FacilityBase(HandleRefModel, AddressModel):
    name: models.CharField = models.CharField(_("Name"), max_length=255, unique=True)
    website: URLField = URLField(_("Website"), blank=True)
    social_media = models.JSONField(_("Social Media"), default=dict, blank=True)

    aka: models.CharField = models.CharField(
        _("Also Known As"), max_length=255, blank=True
    )
    name_long: models.CharField = models.CharField(
        _("Long Name"), max_length=255, blank=True
    )

    clli: models.CharField = models.CharField(_("CLLI Code"), max_length=18, blank=True)
    rencode: models.CharField = models.CharField(
        _("Rencode"), max_length=18, blank=True
    )
    npanxx: models.CharField = models.CharField(_("NPA-NXX"), max_length=21, blank=True)

    tech_email: models.EmailField = models.EmailField(
        _("Technical Email"), max_length=254, blank=True
    )
    tech_phone: models.CharField = models.CharField(
        _("Technical Phone"),
        max_length=192,
        blank=True,
        help_text=const.PHONE_HELP_TEXT,
    )
    sales_email: models.EmailField = models.EmailField(
        _("Sales Email"), max_length=254, blank=True
    )
    sales_phone: models.CharField = models.CharField(
        _("Sales Phone"), max_length=192, blank=True, help_text=const.PHONE_HELP_TEXT
    )

    property: models.CharField = models.CharField(
        _("Property"),
        max_length=27,
        null=True,
        blank=True,
        choices=const.PROPERTY,
        help_text=_(
            "Leasing or renting is an agreement with a property owner for use of the property."
        ),
    )

    diverse_serving_substations: models.BooleanField = models.BooleanField(
        _("Diverse Serving Substations"),
        null=True,
        blank=True,
        help_text=_(
            "Two separate and distinct paths to individual substations which should maintain a separated path back to one or more utility generator stations."
        ),
    )

    available_voltage_services: MultipleChoiceField = MultipleChoiceField(
        _("Available Voltage Services"),
        null=True,
        blank=True,
        max_length=255,
        choices=const.AVAILABLE_VOLTAGE,
        help_text=_(
            "The alternating current voltage available to users of the facility either directly from the landlord or delivered by the utility separately."
        ),
    )

    notes: models.TextField = models.TextField(_("Notes"), blank=True)

    region_continent: models.CharField = models.CharField(
        _("Continental Region"),
        max_length=255,
        choices=const.REGIONS,
        blank=True,
        null=True,
    )

    status_dashboard: URLField = URLField(_("Status Dashboard"), null=True, blank=True)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}facility"
        verbose_name = _("Facility")
        verbose_name_plural = _("Facilities")

    class HandleRef:
        tag = "fac"
        delete_cascade = ["ixfac_set", "netfac_set"]

    def __str__(self):
        return self.name


class ContactBase(HandleRefModel):
    role: models.CharField = models.CharField(
        _("Role"), max_length=27, choices=const.POC_ROLES
    )
    visible: models.CharField = models.CharField(
        _("Visibility"), max_length=64, choices=const.VISIBILITY, default="Public"
    )
    name: models.CharField = models.CharField(_("Name"), max_length=254, blank=True)
    phone: models.CharField = models.CharField(
        _("Phone"), max_length=100, blank=True, help_text=const.PHONE_HELP_TEXT
    )
    email: models.EmailField = models.EmailField(_("Email"), max_length=254, blank=True)
    url: URLField = URLField(_("URL"), blank=True)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}network_contact"
        verbose_name = _("Contact")
        verbose_name_plural = _("Contacts")

    class HandleRef:
        tag = "poc"

    def __str__(self):
        return self.name


class NetworkBase(HandleRefModel):
    asn: ASNField = ASNField(verbose_name="ASN", unique=True)
    name: models.CharField = models.CharField(_("Name"), max_length=255, unique=True)

    aka: models.CharField = models.CharField(
        _("Also Known As"), max_length=255, blank=True
    )
    name_long: models.CharField = models.CharField(
        _("Long Name"), max_length=255, blank=True
    )

    irr_as_set: models.CharField = models.CharField(
        _("IRR as-set/route-set"),
        max_length=255,
        blank=True,
        help_text=_(
            "Reference to an AS-SET or ROUTE-SET in Internet Routing Registry (IRR)"
        ),
    )
    website: URLField = URLField(_("Website"), blank=True)
    social_media = models.JSONField(_("Social Media"), default=dict, blank=True)
    looking_glass: LG_URLField = LG_URLField(_("Looking Glass URL"), blank=True)
    route_server: LG_URLField = LG_URLField(_("Route Server URL"), blank=True)

    notes: models.TextField = models.TextField(_("Notes"), blank=True)
    notes_private: models.TextField = models.TextField(_("Private notes"), blank=True)

    info_traffic: models.CharField = models.CharField(
        _("Traffic Levels"),
        max_length=39,
        blank=True,
        choices=const.TRAFFIC,
        help_text=const.INFO_TRAFFIC_HELP_TEXT,
    )

    info_ratio: models.CharField = models.CharField(
        _("Traffic Ratios"),
        max_length=45,
        blank=True,
        choices=const.RATIOS,
        default="Not Disclosed",
    )
    info_scope: models.CharField = models.CharField(
        _("Geographic Scope"),
        max_length=39,
        blank=True,
        choices=const.SCOPES,
        default="Not Disclosed",
    )

    info_types: MultipleChoiceField = MultipleChoiceField(
        _("Network Types"),
        blank=True,
        max_length=255,
        choices=const.NET_TYPES_MULTI_CHOICE,
    )

    info_prefixes4: models.PositiveIntegerField = models.PositiveIntegerField(
        _("IPv4 Prefixes"),
        null=True,
        blank=True,
        help_text=_(
            "Recommended maximum number of IPv4 "
            "routes/prefixes to be configured on peering "
            "sessions for this ASN"
        ),
    )
    info_prefixes6: models.PositiveIntegerField = models.PositiveIntegerField(
        _("IPv6 Prefixes"),
        null=True,
        blank=True,
        help_text=_(
            "Recommended maximum number of IPv6 "
            "routes/prefixes to be configured on peering "
            "sessions for this ASN"
        ),
    )
    info_unicast: models.BooleanField = models.BooleanField(
        _("Unicast IPv4"), default=False
    )
    info_multicast: models.BooleanField = models.BooleanField(
        _("Multicast"), default=False
    )
    info_ipv6: models.BooleanField = models.BooleanField(
        _("Unicast IPv6"), default=False
    )
    info_never_via_route_servers: models.BooleanField = models.BooleanField(
        _("Never via route servers"),
        default=False,
        help_text=_(
            "Indicates if this network "
            "will announce its routes "
            "via route servers or not"
        ),
    )

    policy_url: URLField = URLField(_("Peering Policy"), blank=True)
    policy_general: models.CharField = models.CharField(
        _("General Policy"),
        max_length=72,
        blank=True,
        choices=const.POLICY_GENERAL,
        help_text=const.POLICY_GENERAL_HELP_TEXT,
    )
    policy_locations: models.CharField = models.CharField(
        _("Multiple Locations"),
        max_length=72,
        blank=True,
        choices=const.POLICY_LOCATIONS,
    )
    policy_ratio: models.BooleanField = models.BooleanField(
        _("Ratio Requirement"), default=False
    )
    policy_contracts: models.CharField = models.CharField(
        _("Contract Requirement"),
        max_length=36,
        blank=True,
        choices=const.POLICY_CONTRACTS,
    )

    status_dashboard: URLField = URLField(_("Status Dashboard"), null=True, blank=True)

    rir_status: models.CharField = models.CharField(
        _("RIR status"),
        null=True,
        default=None,
        max_length=255,
    )
    rir_status_updated: models.DateTimeField = models.DateTimeField(
        _("RIR status updated"), blank=True, null=True
    )

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}network"
        verbose_name = _("Network")
        verbose_name_plural = _("Networks")

    class HandleRef:
        tag = "net"
        delete_cascade = ["poc_set", "netfac_set", "netixlan_set"]

    def __str__(self):
        return self.name


class InternetExchangeBase(HandleRefModel):
    name: models.CharField = models.CharField(_("Name"), max_length=64, unique=True)

    aka: models.CharField = models.CharField(
        _("Also Known As"), max_length=255, blank=True
    )
    name_long: models.CharField = models.CharField(
        _("Long Name"), max_length=255, blank=True
    )

    city: models.CharField = models.CharField(_("City"), max_length=192)
    country: CountryField = CountryField(_("Country"))

    notes: models.TextField = models.TextField(_("Notes"), blank=True)

    region_continent: models.CharField = models.CharField(
        _("Continental Region"), max_length=255, choices=const.REGIONS
    )
    media: models.CharField = models.CharField(
        _("Media Type"), max_length=128, choices=const.MEDIA, default="Ethernet"
    )
    proto_unicast: models.BooleanField = models.BooleanField(
        _("Unicast IPv4"), default=False
    )
    proto_multicast: models.BooleanField = models.BooleanField(
        _("Multicast"), default=False
    )
    proto_ipv6: models.BooleanField = models.BooleanField(
        _("Unicast IPv6"), default=False
    )

    website: URLField = URLField(_("Company Website"), blank=True, default="")
    social_media = models.JSONField(_("Social Media"), default=dict, blank=True)
    url_stats: URLField = URLField(_("Traffic Stats Website"), blank=True)

    tech_email: models.EmailField = models.EmailField(
        _("Technical Email"), max_length=254, blank=True
    )
    tech_phone: models.CharField = models.CharField(
        _("Technical Phone"),
        max_length=192,
        blank=True,
        help_text=const.PHONE_HELP_TEXT,
    )
    policy_email: models.EmailField = models.EmailField(
        _("Policy Email"), max_length=254, blank=True
    )
    policy_phone: models.CharField = models.CharField(
        _("Policy Phone"), max_length=192, blank=True, help_text=const.PHONE_HELP_TEXT
    )

    sales_email: models.EmailField = models.EmailField(
        _("Sales Email"), max_length=254, blank=True
    )
    sales_phone: models.CharField = models.CharField(
        _("Sales Phone"), max_length=192, blank=True, help_text=const.PHONE_HELP_TEXT
    )

    ixf_net_count: models.IntegerField = models.IntegerField(
        _("IX-F Network Count"), default=0
    )
    ixf_last_import: models.DateTimeField = models.DateTimeField(
        _("IX-F Last Import"), null=True, blank=True
    )

    service_level: models.CharField = models.CharField(
        _("Service Level"),
        max_length=60,
        blank=True,
        choices=const.SERVICE_LEVEL_TYPES,
        default="Not Disclosed",
    )

    terms: models.CharField = models.CharField(
        _("Terms"),
        max_length=60,
        blank=True,
        choices=const.TERMS_TYPES,
        default="Not Disclosed",
    )

    status_dashboard: URLField = URLField(_("Status Dashboard"), null=True, blank=True)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}ix"
        verbose_name = _("Internet Exchange")
        verbose_name_plural = _("Internet Exchanges")

    class HandleRef:
        tag = "ix"
        delete_cascade = ["ixfac_set", "ixlan_set"]

    def __str__(self):
        return self.name


class InternetExchangeFacilityBase(HandleRefModel):
    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}ix_facility"
        verbose_name = _("Internet Exchange facility")
        verbose_name_plural = _("Internet Exchange facilities")

    class HandleRef:
        tag = "ixfac"


class IXLanBase(HandleRefModel):
    name: models.CharField = models.CharField(_("Name"), max_length=255, blank=True)
    descr: models.TextField = models.TextField(_("Description"), blank=True)
    mtu: models.PositiveIntegerField = models.PositiveIntegerField(
        "MTU", default=1500, choices=const.MTUS
    )
    vlan: models.PositiveIntegerField = models.PositiveIntegerField(
        "VLAN", null=True, blank=True
    )
    dot1q_support: models.BooleanField = models.BooleanField("802.1Q", default=False)
    rs_asn: ASNField = ASNField(
        verbose_name=_("Route Server ASN"), null=True, blank=True, default=0
    )
    arp_sponge: MacAddressField = MacAddressField(
        verbose_name=_("ARP sponging MAC"), null=True, unique=True, blank=True
    )

    ixf_ixp_member_list_url: models.URLField = models.URLField(
        verbose_name=_("IX-F Member Export URL"), null=True, blank=True
    )
    ixf_ixp_member_list_url_visible: models.CharField = models.CharField(
        verbose_name=_("IX-F Member Export URL Visibility"),
        max_length=64,
        choices=const.VISIBILITY,
        default="Private",
    )

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}ixlan"
        verbose_name = _("Internet Exchange LAN")
        verbose_name_plural = _("Internet Exchange LANs")

    class HandleRef:
        tag = "ixlan"
        delete_cascade = ["ixpfx_set", "netixlan_set"]


class IXLanPrefixBase(HandleRefModel):
    notes: models.CharField = models.CharField(_("Notes"), max_length=255, blank=True)
    protocol: models.CharField = models.CharField(
        _("Protocol"), max_length=64, choices=const.PROTOCOLS
    )
    prefix: IPPrefixField = IPPrefixField(verbose_name=_("Prefix"), unique=True)
    in_dfz: models.BooleanField = models.BooleanField(default=False)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}ixlan_prefix"
        verbose_name = _("Internet Exchange LAN prefix")
        verbose_name_plural = _("Internet Exchange LAN prefixes")

    class HandleRef:
        tag = "ixpfx"


class NetworkFacilityBase(HandleRefModel):
    avail_sonet: models.BooleanField = models.BooleanField("SONET", default=False)
    avail_ethernet: models.BooleanField = models.BooleanField("Ethernet", default=False)
    avail_atm: models.BooleanField = models.BooleanField("ATM", default=False)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}network_facility"
        verbose_name = _("Network Facility")
        verbose_name_plural = _("Network Facilities")

    class HandleRef:
        tag = "netfac"


class NetworkIXLanBase(HandleRefModel):
    asn: ASNField = ASNField(verbose_name="ASN")
    ipaddr4: IPAddressField = IPAddressField(
        verbose_name="IPv4", version=4, blank=True, null=True
    )
    ipaddr6: IPAddressField = IPAddressField(
        verbose_name="IPv6", version=6, blank=True, null=True
    )
    is_rs_peer: models.BooleanField = models.BooleanField(_("RS peer"), default=False)
    bfd_support: models.BooleanField = models.BooleanField(
        _("BFD support"), default=False
    )
    notes: models.CharField = models.CharField(_("Notes"), max_length=255, blank=True)
    speed: models.PositiveIntegerField = models.PositiveIntegerField(
        _("Speed (mbit/sec)")
    )
    operational: models.BooleanField = models.BooleanField(
        _("Operational"), default=True
    )

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}network_ixlan"
        verbose_name = _("Public Peering Exchange Point")
        verbose_name_plural = _("Public Peering Exchange Points")

    class HandleRef:
        tag = "netixlan"


class CarrierBase(HandleRefModel):
    name: models.CharField = models.CharField(_("Name"), max_length=255, unique=True)

    aka: models.CharField = models.CharField(
        _("Also Known As"), max_length=255, blank=True
    )
    name_long: models.CharField = models.CharField(
        _("Long Name"), max_length=255, blank=True
    )

    website: URLField = URLField(_("Website"), blank=True, default="")
    social_media = models.JSONField(_("Social Media"), default=dict, blank=True)
    notes: models.TextField = models.TextField(_("Notes"), blank=True)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}carrier"
        verbose_name = _("Carrier")
        verbose_name_plural = _("Carriers")

    class HandleRef:
        tag = "carrier"
        delete_cascade = ["carrierfac_set"]

    def __str__(self):
        return self.name


class CarrierFacilityBase(HandleRefModel):
    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}carrier_facility"
        verbose_name = _("Carrier presence at facility")
        verbose_name_plural = _("Carrier presences at facility")

    class HandleRef:
        tag = "carrierfac"


class CampusBase(HandleRefModel):
    name: models.CharField = models.CharField(
        _("Campus Name"), max_length=255, unique=True
    )

    name_long: models.CharField = models.CharField(
        _("Long Name"), max_length=255, blank=True, null=True
    )
    aka: models.CharField = models.CharField(
        _("Also Known As"), max_length=255, blank=True, null=True
    )

    website: URLField = URLField(_("Website"), blank=True, default="")
    social_media = models.JSONField(_("Social Media"), default=dict, blank=True)
    notes: models.TextField = models.TextField(_("Notes"), blank=True)

    class Meta:
        abstract = True
        db_table = f"{settings.TABLE_PREFIX}campus"
        verbose_name = _("Campus")
        verbose_name_plural = _("Campuses")

    class HandleRef:
        tag = "campus"

    def __str__(self):
        return self.name
