import os
from urllib.parse import urlparse
from django.db import models
from django.urls import reverse
from django.conf import settings
from django.dispatch.dispatcher import receiver
from django.core.exceptions import ValidationError
from django_countries.fields import CountryField

from ram.models import SimpleBaseModel
from ram.utils import DeduplicatedStorage, get_image_preview, slugify
from ram.managers import PublicManager


class Property(SimpleBaseModel):
    name = models.CharField(max_length=128, unique=True)
    private = models.BooleanField(
        default=False,
        help_text="Property will be only visible to logged users",
    )

    class Meta:
        verbose_name_plural = "Properties"
        ordering = ["name"]

    def __str__(self):
        return self.name

    objects = PublicManager()


class Manufacturer(SimpleBaseModel):
    name = models.CharField(max_length=128)
    slug = models.CharField(max_length=128, unique=True, editable=False)
    category = models.CharField(
        max_length=64, choices=settings.MANUFACTURER_TYPES
    )
    country = CountryField(blank=True)
    website = models.URLField(blank=True)
    logo = models.ImageField(
        upload_to=os.path.join("images", "manufacturers"),
        storage=DeduplicatedStorage,
        null=True,
        blank=True,
    )

    class Meta:
        ordering = ["category", "slug"]
        constraints = [
            models.UniqueConstraint(
                fields=["name", "category"], name="unique_name_category"
            )
        ]
        indexes = [
            # Index for category filtering
            models.Index(fields=["category"], name="mfr_category_idx"),
            # Index for slug lookups
            models.Index(fields=["slug"], name="mfr_slug_idx"),
            # Composite index for category+slug (already in ordering)
            models.Index(
                fields=["category", "slug"], name="mfr_cat_slug_idx"
            ),
        ]

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse(
            "filtered",
            kwargs={
                "_filter": "manufacturer",
                "search": self.slug,
            },
        )

    def website_short(self):
        if self.website:
            return urlparse(self.website).netloc.replace("www.", "")

    def logo_thumbnail(self):
        return get_image_preview(self.logo.url)

    logo_thumbnail.short_description = "Preview"


class Company(SimpleBaseModel):
    name = models.CharField(max_length=64, unique=True)
    slug = models.CharField(max_length=64, unique=True, editable=False)
    extended_name = models.CharField(max_length=128, blank=True)
    country = CountryField()
    freelance = models.BooleanField(default=False)
    logo = models.ImageField(
        upload_to=os.path.join("images", "companies"),
        storage=DeduplicatedStorage,
        null=True,
        blank=True,
    )

    class Meta:
        verbose_name_plural = "Companies"
        ordering = ["slug"]
        indexes = [
            # Index for slug lookups (used frequently in URLs)
            models.Index(fields=["slug"], name="company_slug_idx"),
            # Index for country filtering
            models.Index(fields=["country"], name="company_country_idx"),
            # Index for freelance filtering
            models.Index(fields=["freelance"], name="company_freelance_idx"),
        ]

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse(
            "filtered",
            kwargs={
                "_filter": "company",
                "search": self.slug,
            },
        )

    def extended_name_pp(self):
        return "({})".format(self.extended_name) if self.extended_name else ""

    def logo_thumbnail(self):
        return get_image_preview(self.logo.url)

    logo_thumbnail.short_description = "Preview"


class Decoder(SimpleBaseModel):
    name = models.CharField(max_length=128, unique=True)
    manufacturer = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
        limit_choices_to={"category": "accessory"},
    )
    version = models.CharField(max_length=64, blank=True)
    sound = models.BooleanField(default=False)
    image = models.ImageField(
        upload_to=os.path.join("images", "decoders"),
        storage=DeduplicatedStorage,
        null=True,
        blank=True,
    )

    class Meta:
        ordering = ["manufacturer__name", "name"]

    def __str__(self):
        return "{0} - {1}".format(self.manufacturer, self.name)

    def image_thumbnail(self):
        return get_image_preview(self.image.url)

    image_thumbnail.short_description = "Preview"


def calculate_ratio(ratio):
    try:
        num, den = ratio.split(":")
        return int(num) / float(den) * 10000
    except (ValueError, ZeroDivisionError):
        raise ValidationError("Invalid ratio format")


class Scale(SimpleBaseModel):
    scale = models.CharField(max_length=32, unique=True)
    slug = models.CharField(max_length=32, unique=True, editable=False)
    ratio = models.CharField(max_length=16, validators=[calculate_ratio])
    ratio_int = models.SmallIntegerField(editable=False, default=0)
    tracks = models.FloatField(
        help_text="Distance between model tracks in mm",
    )
    gauge = models.CharField(
        max_length=16,
        blank=True,
        help_text="Distance between real tracks. Please specify the unit (mm, in, ...)",  # noqa: E501
    )

    class Meta:
        ordering = ["-ratio_int", "-tracks", "scale"]
        indexes = [
            # Index for slug lookups
            models.Index(fields=["slug"], name="scale_slug_idx"),
            # Index for ratio_int ordering and filtering
            models.Index(fields=["ratio_int"], name="scale_ratio_idx"),
            # Composite index for common ordering pattern
            models.Index(
                fields=["-ratio_int", "-tracks"], name="scale_ratio_tracks_idx"
            ),
        ]

    def get_absolute_url(self):
        return reverse(
            "filtered",
            kwargs={
                "_filter": "scale",
                "search": self.slug,
            },
        )

    def __str__(self):
        return str(self.scale)


@receiver(models.signals.pre_save, sender=Scale)
def scale_save(sender, instance, **kwargs):
    instance.ratio_int = calculate_ratio(instance.ratio)


class RollingStockType(SimpleBaseModel):
    type = models.CharField(max_length=64)
    order = models.PositiveSmallIntegerField()
    category = models.CharField(
        max_length=64, choices=settings.ROLLING_STOCK_TYPES
    )
    slug = models.CharField(max_length=128, unique=True, editable=False)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["category", "type"],
                name="unique_category_type"
            )
        ]
        ordering = ["order"]

    def get_absolute_url(self):
        return reverse(
            "filtered",
            kwargs={
                "_filter": "type",
                "search": self.slug,
            },
        )

    def __str__(self):
        return "{0} {1}".format(self.type, self.category)


class Tag(SimpleBaseModel):
    name = models.CharField(max_length=128, unique=True)
    slug = models.CharField(max_length=128, unique=True)

    class Meta:
        ordering = ["slug"]

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse(
            "filtered",
            kwargs={
                "_filter": "tag",
                "search": self.slug,
            },
        )


class Shop(SimpleBaseModel):
    name = models.CharField(max_length=128, unique=True)
    country = CountryField(blank=True)
    website = models.URLField(blank=True)
    on_line = models.BooleanField(default=True)
    active = models.BooleanField(default=True)

    class Meta:
        ordering = [models.functions.Lower("name"),]

    def __str__(self):
        return self.name


@receiver(models.signals.pre_save, sender=Manufacturer)
@receiver(models.signals.pre_save, sender=Company)
@receiver(models.signals.pre_save, sender=Scale)
@receiver(models.signals.pre_save, sender=RollingStockType)
@receiver(models.signals.pre_save, sender=Tag)
def slug_pre_save(sender, instance, **kwargs):
    instance.slug = slugify(instance.__str__())
