from decimal import Decimal

from auditlog.registry import auditlog
from django.core.validators import MinValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _

from servala.core.models.mixins import ServalaModelMixin


class ComputePlan(ServalaModelMixin):
    """
    Compute resource plans for service instances.

    Defines CPU and memory allocations. Pricing and service level are configured
    per assignment to a ControlPlaneCRD.
    """

    name = models.CharField(
        max_length=100,
        verbose_name=_("Name"),
    )
    description = models.TextField(
        blank=True,
        verbose_name=_("Description"),
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_("Is active"),
        help_text=_("Whether this plan is available for selection"),
    )

    memory_requests = models.CharField(
        max_length=20,
        verbose_name=_("Memory requests"),
    )
    memory_limits = models.CharField(
        max_length=20,
        verbose_name=_("Memory limits"),
    )
    cpu_requests = models.CharField(
        max_length=20,
        verbose_name=_("CPU requests"),
    )
    cpu_limits = models.CharField(
        max_length=20,
        verbose_name=_("CPU limits"),
    )

    class Meta:
        verbose_name = _("Compute Plan")
        verbose_name_plural = _("Compute Plans")
        ordering = ["name"]

    def __str__(self):
        return self.name

    def get_resource_summary(self):
        return f"{self.cpu_limits} vCPU, {self.memory_limits} RAM"


class ComputePlanAssignment(ServalaModelMixin):
    """
    Links compute plans to control plane CRDs with pricing and service level.

    A product in Odoo represents a service with a specific compute plan, control plane,
    and SLA. This model stores that correlation. The same compute plan can be assigned
    multiple times to the same CRD with different SLAs and pricing.
    """

    SLA_CHOICES = [
        ("besteffort", _("Best Effort")),
        ("guaranteed", _("Guaranteed Availability")),
    ]

    compute_plan = models.ForeignKey(
        ComputePlan,
        on_delete=models.CASCADE,
        related_name="assignments",
        verbose_name=_("Compute plan"),
    )
    control_plane_crd = models.ForeignKey(
        "ControlPlaneCRD",
        on_delete=models.CASCADE,
        related_name="compute_plan_assignments",
        verbose_name=_("Control plane CRD"),
    )
    sla = models.CharField(
        max_length=20,
        choices=SLA_CHOICES,
        verbose_name=_("SLA"),
        help_text=_("Service Level Agreement"),
    )
    odoo_product_id = models.CharField(
        max_length=255,
        verbose_name=_("Odoo product ID"),
        help_text=_(
            "Product ID in Odoo (e.g., 'openshift-exoscale-workervcpu-standard')"
        ),
    )
    odoo_unit_id = models.CharField(
        max_length=255,
        verbose_name=_("Odoo unit ID"),
    )
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(Decimal("0.00"))],
        verbose_name=_("Price"),
        help_text=_("Price per unit"),
    )

    BILLING_UNIT_CHOICES = [
        ("hour", _("Hour")),
        ("day", _("Day")),
        ("month", _("Month (30 days / 720 hours)")),
        ("year", _("Year")),
    ]
    unit = models.CharField(
        max_length=10,
        choices=BILLING_UNIT_CHOICES,
        default="hour",
        verbose_name=_("Billing unit"),
        help_text=_("Unit for the price (e.g., price per hour)"),
    )

    minimum_service_size = models.PositiveIntegerField(
        default=1,
        validators=[MinValueValidator(1)],
        verbose_name=_("Minimum service size"),
        help_text=_(
            "Minimum value for spec.parameters.instances "
            "(Guaranteed Availability may require multiple instances)"
        ),
    )
    sort_order = models.PositiveIntegerField(
        default=0,
        verbose_name=_("Sort order"),
        help_text=_("Order in which plans are displayed to users"),
    )
    is_active = models.BooleanField(
        default=True,
        verbose_name=_("Is active"),
        help_text=_("Whether this plan is available for this CRD"),
    )

    class Meta:
        verbose_name = _("Compute Plan Assignment")
        verbose_name_plural = _("Compute Plan Assignments")
        unique_together = [["compute_plan", "control_plane_crd", "sla"]]
        ordering = ["sort_order", "compute_plan__name", "sla"]

    def __str__(self):
        return f"{self.compute_plan.name} ({self.get_sla_display()}) → {self.control_plane_crd}"

    def get_odoo_reporting_product_id(self):
        # TODO: Implement Odoo cache lookup when OdooObjectCache is integrated
        # For now, just return the product ID
        return self.odoo_product_id


auditlog.register(ComputePlan, exclude_fields=["updated_at"], serialize_data=True)
auditlog.register(
    ComputePlanAssignment, exclude_fields=["updated_at"], serialize_data=True
)
