# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models
from polymorphic import PolymorphicModel
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from netfields import InetAddressField, NetManager

from coin.configuration.models import Configuration
# from coin.offers.backends import ValidateBackendType
from coin import validation

FINGERPRINT_TYPES = (
    ('ED25519', 'ED25519'),
    ('RSA', 'RSA'),
    ('ECDSA', 'ECDSA')
)

PROTOCOLE_TYPES = (
    ('VNC', 'VNC'),
)


class VPSConfiguration(Configuration):
    url_namespace = "vps"
    activated = models.BooleanField(default=False, verbose_name='activé')
    ipv4_endpoint = InetAddressField(validators=[validation.validate_v4],
                                     verbose_name="IPv4", blank=True, null=True,
                                     help_text="Adresse IPv4 utilisée par "
                                     "défaut sur le VPS")
    ipv6_endpoint = InetAddressField(validators=[validation.validate_v6],
                                     verbose_name="IPv6", blank=True, null=True,
                                     help_text="Adresse IPv6 utilisée par "
                                     "défaut sur le VPS")
    objects = NetManager()

    def get_absolute_url(self):
        return reverse('vps:details', args=[str(self.pk)])

    # This method is part of the general configuration interface.
    def subnet_event(self):
        self.check_endpoints(delete=True)
        # We potentially changed the endpoints, so we need to save.  Also,
        # saving will update the subnets in the LDAP backend.
        self.full_clean()
        self.save()

    def get_subnets(self, version):
        subnets = self.ip_subnet.all()
        return [subnet for subnet in subnets if subnet.inet.version == version]

    def generate_endpoints(self, v4=True, v6=True):
        """Generate IP endpoints in one of the attributed IP subnets.  If there is
        no available subnet for a given address family, then no endpoint
        is generated for this address family.  If there already is an
        endpoint, do nothing.

        Returns True if an endpoint was generated.

        TODO: this should be factored for other technologies (DSL, etc)

        """
        subnets = self.ip_subnet.all()
        updated = False
        if v4 and self.ipv4_endpoint is None:
            subnets_v4 = [s for s in subnets if s.inet.version == 4]
            if len(subnets_v4) > 0:
                self.ipv4_endpoint = subnets_v4[0].inet.ip
                updated = True
        if v6 and self.ipv6_endpoint is None:
            subnets_v6 = [s for s in subnets if s.inet.version == 6]
            if len(subnets_v6) > 0:
                # With v6, we choose the second host of the subnet (cafe::1)
                gen = subnets_v6[0].inet.iter_hosts()
                gen.next()
                self.ipv6_endpoint = gen.next()
                updated = True
        return updated

    def check_endpoints(self, delete=False):
        """Check that the IP endpoints are included in one of the attributed IP
        subnets.

        If [delete] is True, then simply delete the faulty endpoints
        instead of raising an exception.
        """
        error = "L'IP {} n'est pas dans un réseau attribué."
        subnets = self.ip_subnet.all()
        is_faulty = lambda endpoint : endpoint and not any([endpoint in subnet.inet for subnet in subnets])
        if is_faulty(self.ipv4_endpoint):
            if delete:
                self.ipv4_endpoint = None
            else:
                raise ValidationError(error.format(self.ipv4_endpoint))
        if is_faulty(self.ipv6_endpoint):
            if delete:
                self.ipv6_endpoint = None
            else:
                raise ValidationError(error.format(self.ipv6_endpoint))

    def clean(self):
        # If saving for the first time and IP endpoints are not specified,
        # generate them automatically.
        if self.pk is None:
            self.generate_endpoints()
        self.check_endpoints()

    def __unicode__(self):
        return 'VPS ' #+ self.login

    class Meta:
        verbose_name = 'VPS'

class FingerPrint(PolymorphicModel):
    vps = models.ForeignKey(VPSConfiguration, verbose_name="vps")
    algo = models.CharField(max_length=256, verbose_name="algo",
                            choices=FINGERPRINT_TYPES)
    fingerprint = models.CharField(max_length=256, verbose_name="empreinte")
    length = models.IntegerField(verbose_name="longueur de la clé", null=True)

    class Meta:
        verbose_name = 'Empreinte'


class Console(models.Model):
    vps = models.OneToOneField(VPSConfiguration, verbose_name="vps")
    protocol = models.CharField(max_length=256, verbose_name="protocole",
                            choices=PROTOCOLE_TYPES)
    domain = models.CharField(max_length=256, verbose_name="nom de domaine",
                                blank=True, null=True)
    port = models.IntegerField(verbose_name="port", null=True)
    password_link = models.URLField(verbose_name="Mot de passe", blank=True,
                           null=True, help_text="Lien à usage unique (détruit après ouverture)")
    class Meta:
        verbose_name = 'Console'