from django.db import models
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.urls import reverse
from django.utils.text import slugify
from django_prose_editor.fields import ProseEditorField

from .base import (
    Category,
    ReusableText,
    ManagedServiceProvider,
    validate_image_size,
    Currency,
)
from .providers import CloudProvider
from .images import ImageReference


class Service(ImageReference):
    name = models.CharField(max_length=200)
    slug = models.SlugField(max_length=250, unique=True)
    description = ProseEditorField()
    tagline = models.TextField(max_length=500, blank=True, null=True)
    # Original logo field - keep temporarily for migration
    logo = models.ImageField(
        upload_to="service_logos/",
        validators=[validate_image_size],
        null=True,
        blank=True,
    )
    categories = models.ManyToManyField(Category, related_name="services")
    features = ProseEditorField()
    is_featured = models.BooleanField(default=False)
    is_coming_soon = models.BooleanField(default=False)
    disable_listing = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    def clean(self):
        if self.is_featured and self.is_coming_soon:
            raise ValidationError(
                "A service cannot be both featured and coming soon simultaneously."
            )
        super().clean()

    def save(self, *args, **kwargs):
        self.clean()  # Ensure validation runs on save
        if not self.slug:
            self.slug = slugify(self.name)
            counter = 1
            while Service.objects.filter(slug=self.slug).exists():
                self.slug = f"{slugify(self.name)}-{counter}"
                counter += 1
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse("services:service_detail", kwargs={"slug": self.slug})

    @property
    def get_logo(self):
        """Returns the logo from library or falls back to legacy logo"""
        if self.image_library and self.image_library.image:
            return self.image_library.image
        return self.logo


class ServiceOffering(models.Model):
    service = models.ForeignKey(
        Service, on_delete=models.CASCADE, related_name="offerings"
    )
    msp = models.CharField(
        "Managed Service Provider",
        max_length=2,
        default=ManagedServiceProvider.VS,
        choices=ManagedServiceProvider.choices,
    )
    cloud_provider = models.ForeignKey(
        CloudProvider, on_delete=models.CASCADE, related_name="offerings"
    )
    description = ProseEditorField(blank=True, null=True)
    offer_description = models.ForeignKey(
        ReusableText,
        on_delete=models.PROTECT,
        related_name="offer_descriptions",
        blank=True,
        null=True,
    )

    disable_listing = models.BooleanField(default=False)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        unique_together = ["service", "cloud_provider"]
        ordering = ["service__name", "cloud_provider__name"]

    def __str__(self):
        return f"{self.service.name} on {self.cloud_provider.name}"

    def get_absolute_url(self):
        return reverse(
            "services:offering_detail",
            kwargs={
                "provider_slug": self.cloud_provider.slug,
                "service_slug": self.service.slug,
            },
        )


class PlanPrice(models.Model):
    plan = models.ForeignKey(
        "Plan", on_delete=models.CASCADE, related_name="plan_prices"
    )
    currency = models.CharField(
        max_length=3,
        choices=Currency.choices,
    )
    amount = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        help_text="Price in the specified currency, excl. VAT",
    )

    class Meta:
        unique_together = ("plan", "currency")
        ordering = ["currency"]

    def __str__(self):
        return f"{self.plan.name} - {self.amount} {self.currency}"


class Plan(models.Model):
    name = models.CharField(max_length=100)
    description = ProseEditorField(blank=True, null=True)
    plan_description = models.ForeignKey(
        ReusableText,
        on_delete=models.PROTECT,
        related_name="plan_descriptions",
        blank=True,
        null=True,
    )
    offering = models.ForeignKey(
        ServiceOffering, on_delete=models.CASCADE, related_name="plans"
    )

    # Ordering and highlighting fields
    order = models.PositiveIntegerField(
        default=0,
        help_text="Order of this plan in the offering (lower numbers appear first)",
    )
    is_best = models.BooleanField(
        default=False, help_text="Mark this plan as the best/recommended option"
    )

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order", "name"]
        unique_together = [["offering", "name"]]

    def __str__(self):
        return f"{self.offering} - {self.name}"

    def clean(self):
        # Ensure only one plan per offering can be marked as "best"
        if self.is_best:
            existing_best = Plan.objects.filter(
                offering=self.offering, is_best=True
            ).exclude(pk=self.pk)
            if existing_best.exists():
                from django.core.exceptions import ValidationError

                raise ValidationError(
                    "Only one plan per offering can be marked as the best option."
                )

    def save(self, *args, **kwargs):
        self.clean()
        super().save(*args, **kwargs)

    def get_price(self, currency_code: str):
        price_obj = PlanPrice.objects.filter(plan=self, currency=currency_code).first()
        if price_obj:
            return price_obj.amount
        return None


class ExternalLinkOffering(models.Model):
    offering = models.ForeignKey(
        ServiceOffering, on_delete=models.CASCADE, related_name="external_links"
    )
    url = models.URLField()
    description = models.CharField(max_length=200)
    order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order", "description"]
        verbose_name = "External Link"
        verbose_name_plural = "External Links"

    def __str__(self):
        return f"{self.description} ({self.url})"

    def clean(self):
        validate = URLValidator()
        try:
            validate(self.url)
        except ValidationError:
            raise ValidationError({"url": "Enter a valid URL."})


class ExternalLink(models.Model):
    service = models.ForeignKey(
        Service, on_delete=models.CASCADE, related_name="external_links"
    )
    url = models.URLField()
    description = models.CharField(max_length=200)
    order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["order", "description"]
        verbose_name = "External Link"
        verbose_name_plural = "External Links"

    def __str__(self):
        return f"{self.description} ({self.url})"

    def clean(self):
        validate = URLValidator()
        try:
            validate(self.url)
        except ValidationError:
            raise ValidationError({"url": "Enter a valid URL."})
