from django.db import models
from decimal import Decimal
from sitemanagement.constants.image_file_path import register_object_upload_path

class Multiplexor(models.Model):
    """Устройство-мультиплексор"""
    name = models.CharField(max_length=50, unique=True)
    ip = models.GenericIPAddressField(null=True, blank=True)
    subnet = models.GenericIPAddressField(null=True, blank=True)
    gateway = models.GenericIPAddressField(null=True, blank=True)
    sd_path = models.CharField(max_length=255, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    class Meta:
        verbose_name = "Мультиплексор"
        verbose_name_plural = "Мультиплексоры"
        ordering = ["name"]

    def __str__(self):
        return self.name


class Channel(models.Model):
    """Физический канал мультиплексора"""
    multiplexor = models.ForeignKey(Multiplexor, on_delete=models.CASCADE, related_name="channels")
    number = models.PositiveSmallIntegerField()  # CH-1 ... CH-14
    # для "полканалов" можно использовать дробное значение (1, 1.5, 2, ...)
    position = models.DecimalField(max_digits=4, decimal_places=1, default=Decimal("1.0"))
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    class Meta:
        unique_together = ("multiplexor", "number", "position")
        verbose_name = "Канал"
        verbose_name_plural = "Каналы"
        ordering = ["multiplexor", "number", "position"]
        
    def __str__(self):
        return f"{self.multiplexor.name} - CH{self.number} ({self.position})"


class SensorType(models.Model):
    """Тип датчика: GA, PE, GLE"""
    code = models.CharField(max_length=10, unique=True)  # GA, PE, GLE
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True, null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    # пороговые значения для всех сенсоров этого типа
    min_value = models.DecimalField(max_digits=12, decimal_places=4, null=True, blank=True)
    max_value = models.DecimalField(max_digits=12, decimal_places=4, null=True, blank=True)
    
    class Meta:
        verbose_name = "Тип сенсора"
        verbose_name_plural = "Типы сенсоров"
        ordering = ["name"]

    def __str__(self):
        return self.code


class SignalFormat(models.Model):
    """Формат сигнала и правило преобразования"""
    sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, related_name="formats")
    code = models.CharField(max_length=50, verbose_name="Код")  # например "4-20мА", "VW f<1600Hz", "NTC R>250Ohm"
    unit = models.CharField(max_length=20, blank=True, null=True, verbose_name="Единица измерения")  # °C, мкм/м, мм и т.п.
    conversion_rule = models.CharField(max_length=255, blank=True, null=True, verbose_name="Правило преобразования")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    class Meta:
        unique_together = ("sensor_type", "code")
        verbose_name = "Формат сигнала"
        verbose_name_plural = "Форматы сигналов"
        ordering = ["sensor_type", "code"]
        
    def __str__(self):
        return f"{self.sensor_type.code} - {self.code}"


class Sensor(models.Model):
    """Конкретный датчик, установленный в канале"""
    channel = models.ForeignKey(Channel, on_delete=models.CASCADE, related_name="sensors")
    sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, verbose_name="Тип сенсора")
    signal_format = models.ForeignKey(SignalFormat, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Формат сигнала")
    serial_number = models.CharField(max_length=50, blank=True, null=True, verbose_name="Серийный номер")  # CL 2106009
    name = models.CharField(max_length=50, blank=True, null=True, verbose_name="Название")  # GA-1, HLE-1 и т.п.
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    math_formula = models.CharField(null=True, blank=True, max_length=255, verbose_name="Математическая формула")
    
    class Meta:
        verbose_name = "Датчик"
        verbose_name_plural = "Датчики"
        ordering = ["channel", "sensor_type", "serial_number"]

    def __str__(self):
        return f"{self.name or self.sensor_type.code} ({self.serial_number})"


class Metric(models.Model):
    """Значения, которые приходят из CSV"""
    timestamp = models.DateTimeField()
    sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name="metrics", verbose_name="Датчик")
    raw_value = models.CharField(max_length=50, verbose_name="Исходное значение")  # исходное значение из файла (например "11.964 (A)")
    value = models.FloatField(null=True, blank=True, verbose_name="Преобразованное значение")  # преобразованное значение
    status = models.CharField(max_length=20, blank=True, null=True, verbose_name="Статус")  # No Rx, Error, NotAv и т.д.
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        verbose_name = "Метрика"
        verbose_name_plural = "Метрики"
        ordering = ["timestamp", "sensor"]
        indexes = [
            models.Index(fields=["timestamp"]),
            models.Index(fields=["sensor"]),
        ]
        
    def __str__(self):
        return f"{self.timestamp} {self.sensor} = {self.value} {self.sensor.signal_format.unit if self.sensor.signal_format else ''}"


class Alert(models.Model):
    """Тревоги по метрикам"""
    sensor = models.ForeignKey(Sensor, on_delete=models.CASCADE, related_name="alerts", verbose_name="Датчик")
    metric = models.ForeignKey(Metric, on_delete=models.CASCADE, related_name="alerts", verbose_name="Метрика")
    sensor_type = models.ForeignKey(SensorType, on_delete=models.CASCADE, related_name="alerts", verbose_name="Тип сенсора")
    message = models.CharField(max_length=255, verbose_name="Сообщение")
    severity = models.CharField(
        max_length=20,
        choices=[
            ("warning", "Warning"),
            ("critical", "Critical"),
        ],
        default="warning",
        verbose_name="Уровень тревоги"
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    resolved = models.BooleanField(default=False, verbose_name="Статус обработки")

    class Meta:
        indexes = [
            models.Index(fields=["created_at"]),
            models.Index(fields=["sensor"]),
            models.Index(fields=["sensor_type"]),
        ]
        verbose_name = "Тревога"
        verbose_name_plural = "Тревоги"
        ordering = ["created_at", "sensor"]
        
    def __str__(self):
        return f"ALERT {self.sensor} @ {self.metric.timestamp}: {self.message}"
    
class Object(models.Model):
    """Объект"""
    title = models.CharField(max_length=255, verbose_name="Название")
    description = models.TextField(blank=True, null=True, verbose_name="Описание")
    image = models.ImageField(upload_to=register_object_upload_path, null=True, blank=True, verbose_name="Изображение")
    address = models.CharField(max_length=255, verbose_name="Адрес")
    floors = models.PositiveSmallIntegerField(verbose_name="Количество этажей")
    area = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Площадь")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        verbose_name = "Объект"
        verbose_name_plural = "Объекты"
        ordering = ["title"]
        
    def __str__(self):
        return self.title

class Zone(models.Model):
    """Зона"""
    object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name="zones", verbose_name="Объект")
    name = models.CharField(max_length=255, verbose_name="Название")
    floor = models.PositiveSmallIntegerField(verbose_name="Этаж")
    sensors = models.ManyToManyField(Sensor, related_name="zones", verbose_name="Датчики")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        verbose_name = "Зона"
        verbose_name_plural = "Зоны"
        ordering = ["object", "floor", "name"]  # сортировка сначала по объекту, потом по этажу
        
    def clean(self):
        from django.core.exceptions import ValidationError
        # проверяем что номер этажа не превышает количество этажей в здании
        if self.floor > self.object.floors:
            raise ValidationError({
                'floor': f'Номер этажа не может быть больше количества этажей в здании ({self.object.floors})'
            })

    def __str__(self):
        return f"{self.object.title} - Этаж {self.floor} - {self.name}"
    