"""Abstract base models for scoring rubrics shared across SP2/SP4/SP5.

These are *abstract* — no DB table is created from this module. Concrete
subclasses in each SP add the FK to their parent record (Programme,
InnovationChallenge, FundingCall) and any SP-specific extras.

Why not inherit SP2's existing ScoringRubric/RubricSection directly:
SP2's models are concrete with FK ``programme = OneToOneField(Programme)``.
A new SP cannot subclass them without inheriting that FK. Abstract bases
give us shared shape (weight, max_marks, ordering) while leaving the
parent-FK choice to each subclass.
"""
from __future__ import annotations

from decimal import Decimal

from django.db import models


class BaseScoringRubric(models.Model):
    """Mandatory before publishing a call (FRSME-INC004, ISD002, INV004).

    Subclasses MUST add:
        ``parent = OneToOneField(<their call type>, related_name="rubric")``

    The ``sections`` reverse accessor is provided by ``BaseRubricSection``
    subclasses via their ``related_name="sections"``.
    """

    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

    @property
    def total_weight(self) -> Decimal:
        # `sections` resolves on the concrete subclass.
        return self.sections.aggregate(total=models.Sum("weight"))["total"] or Decimal("0")  # type: ignore[attr-defined]

    @property
    def is_complete(self) -> bool:
        """A rubric is publishable iff its sections sum to 1.0 ± 0.001."""
        return (
            self.sections.exists()  # type: ignore[attr-defined]
            and abs(self.total_weight - Decimal("1")) < Decimal("0.001")
        )


class BaseRubricSection(models.Model):
    """One section of a rubric. Subclasses add the FK back to their concrete
    rubric, e.g. ``rubric = ForeignKey(MyRubric, related_name="sections")``.
    """

    title = models.CharField(max_length=255)
    instructions = models.TextField(blank=True)
    weight = models.DecimalField(
        max_digits=5,
        decimal_places=4,
        default=Decimal("0.25"),
        help_text="Fractional weight (sections must sum to 1.0).",
    )
    max_marks = models.PositiveSmallIntegerField(default=10)
    order = models.PositiveSmallIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True
        ordering = ["order", "id"]

    def __str__(self) -> str:  # pragma: no cover - admin display only
        return self.title
