"""
revenue/models.py
=================
Models for Module 4 — Income & Revenue Management.

Hierarchy:
  RevenueHead → RevenueSubHead → RevenueItem
                                      ↓
                              RevenueRecord  (actual transaction)

Revenue codes follow the same auto-generation pattern as budget:
  Head: INV  SubHead: INV-LAB  Item: INV-LAB-001
"""

import re

from django.contrib.auth.models import Group, User
from django.db import models
from django.urls import reverse_lazy
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _

from base.models import Department
from budget.models import BudgetItem
from horilla.models import HorillaModel


# ---------------------------------------------------------------------------
# Code generation (reuses same logic as budget app)
# ---------------------------------------------------------------------------


def _make_abbreviation(name: str, max_len: int = 5) -> str:
    words = re.sub(r"[^a-zA-Z0-9\s]", "", name).split()
    if not words:
        return "X"
    abbr = "".join(w[0] for w in words).upper()[:max_len] if len(words) > 1 else words[0][:max_len].upper()
    return abbr or "X"


def _unique_code(base: str, model_class, field: str = "code", instance_pk=None) -> str:
    code, counter = base, 2
    qs = model_class.objects.filter(**{field: code})
    if instance_pk:
        qs = qs.exclude(pk=instance_pk)
    while qs.exists():
        code = f"{base}{counter}"
        counter += 1
        qs = model_class.objects.filter(**{field: code})
        if instance_pk:
            qs = qs.exclude(pk=instance_pk)
    return code


# ---------------------------------------------------------------------------
# RevenueHead
# ---------------------------------------------------------------------------


class RevenueHead(HorillaModel):
    """
    Top-level revenue category (e.g. 'Investigation', 'Consultation').
    """

    name = models.CharField(max_length=200, unique=True, verbose_name=_("Head Name"))
    code = models.CharField(max_length=20, unique=True, editable=False, verbose_name=_("Revenue Code"))
    description = models.TextField(blank=True, verbose_name=_("Description"))
    allowed_recorder_group = models.ForeignKey(
        Group,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_("Allowed Recorder Group"),
        help_text=_("Only members of this group can record revenue under this head. Leave blank to allow all."),
    )

    class Meta:
        verbose_name = _("Revenue Head")
        verbose_name_plural = _("Revenue Heads")
        ordering = ["name"]

    def save(self, *args, **kwargs):
        if not self.code:
            base = _make_abbreviation(self.name)
            self.code = _unique_code(base, RevenueHead, instance_pk=self.pk)
        super().save(*args, **kwargs)

    def __str__(self):
        return f"[{self.code}] {self.name}"

    # ------------------------------------------------------------------
    # Horilla URL helpers
    # ------------------------------------------------------------------

    def get_absolute_url(self):
        return reverse_lazy("revenue-head-list")

    def get_update_url(self):
        return reverse_lazy("revenue-head-edit", kwargs={"pk": self.pk})

    def get_delete_url(self):
        return reverse_lazy("revenue-head-delete", kwargs={"pk": self.pk})

    def get_delete_instance(self):
        return self.pk

    def sub_head_count(self):
        count = self.sub_heads.filter(is_active=True).count()
        return format_html('<span class="badge oh-badge oh-badge--primary">{}</span>', count)

    sub_head_count.short_description = _("Sub-Heads")


# ---------------------------------------------------------------------------
# RevenueSubHead
# ---------------------------------------------------------------------------


class RevenueSubHead(HorillaModel):
    """
    Second-level revenue category (e.g. 'Laboratory', 'Radiology').
    """

    head = models.ForeignKey(
        RevenueHead,
        on_delete=models.CASCADE,
        related_name="sub_heads",
        verbose_name=_("Revenue Head"),
    )
    name = models.CharField(max_length=200, verbose_name=_("Sub-Head Name"))
    code = models.CharField(max_length=30, unique=True, editable=False, verbose_name=_("Revenue Code"))
    description = models.TextField(blank=True, verbose_name=_("Description"))

    class Meta:
        verbose_name = _("Revenue Sub-Head")
        verbose_name_plural = _("Revenue Sub-Heads")
        ordering = ["head__name", "name"]
        unique_together = [["head", "name"]]

    def save(self, *args, **kwargs):
        if not self.code:
            abbr = _make_abbreviation(self.name)
            base = f"{self.head.code}-{abbr}"
            self.code = _unique_code(base, RevenueSubHead, instance_pk=self.pk)
        super().save(*args, **kwargs)

    def __str__(self):
        return f"[{self.code}] {self.name}"

    # ------------------------------------------------------------------
    # Horilla URL helpers
    # ------------------------------------------------------------------

    def get_absolute_url(self):
        return reverse_lazy("revenue-sub-head-list")

    def get_update_url(self):
        return reverse_lazy("revenue-sub-head-edit", kwargs={"pk": self.pk})

    def get_delete_url(self):
        return reverse_lazy("revenue-sub-head-delete", kwargs={"pk": self.pk})

    def get_delete_instance(self):
        return self.pk

    def item_count(self):
        count = self.items.filter(is_active=True).count()
        return format_html('<span class="badge oh-badge oh-badge--primary">{}</span>', count)

    item_count.short_description = _("Items")


# ---------------------------------------------------------------------------
# RevenueItem
# ---------------------------------------------------------------------------


class RevenueItem(HorillaModel):
    """
    Leaf-level revenue line item (e.g. 'MCS', 'Pregnancy Scan').
    Optionally maps to a BudgetItem for budget-vs-actual analysis.
    """

    sub_head = models.ForeignKey(
        RevenueSubHead,
        on_delete=models.CASCADE,
        related_name="items",
        verbose_name=_("Revenue Sub-Head"),
    )
    name = models.CharField(max_length=200, verbose_name=_("Item Name"))
    code = models.CharField(max_length=40, unique=True, editable=False, verbose_name=_("Revenue Code"))
    description = models.TextField(blank=True, verbose_name=_("Description"))
    budget_item = models.ForeignKey(
        BudgetItem,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="revenue_items",
        verbose_name=_("Linked Budget Item"),
        help_text=_("Optional. Links this revenue item to a budget item for budget-vs-actual analysis."),
    )

    class Meta:
        verbose_name = _("Revenue Item")
        verbose_name_plural = _("Revenue Items")
        ordering = ["sub_head__head__name", "sub_head__name", "name"]
        unique_together = [["sub_head", "name"]]

    def save(self, *args, **kwargs):
        if not self.code:
            last = (
                RevenueItem.objects.filter(
                    sub_head=self.sub_head, code__startswith=self.sub_head.code
                )
                .order_by("code")
                .last()
            )
            if last:
                try:
                    last_seq = int(last.code.rsplit("-", 1)[-1])
                except ValueError:
                    last_seq = 0
            else:
                last_seq = 0
            self.code = f"{self.sub_head.code}-{last_seq + 1:03d}"
        super().save(*args, **kwargs)

    def __str__(self):
        return f"[{self.code}] {self.name}"

    # ------------------------------------------------------------------
    # Horilla URL helpers
    # ------------------------------------------------------------------

    def get_absolute_url(self):
        return reverse_lazy("revenue-item-list")

    def get_update_url(self):
        return reverse_lazy("revenue-item-edit", kwargs={"pk": self.pk})

    def get_delete_url(self):
        return reverse_lazy("revenue-item-delete", kwargs={"pk": self.pk})

    def get_delete_instance(self):
        return self.pk

    def record_count(self):
        count = self.records.count()
        return format_html('<span class="badge oh-badge oh-badge--primary">{}</span>', count)

    record_count.short_description = _("Records")


# ---------------------------------------------------------------------------
# RevenueRecord  (actual transaction)
# ---------------------------------------------------------------------------


def _generate_rev_reference():
    """Auto-generate reference: REV-YYYY-NNNNNN"""
    from datetime import date
    year = date.today().year
    prefix = f"REV-{year}-"
    last = (
        RevenueRecord.objects.filter(reference_number__startswith=prefix)
        .order_by("reference_number")
        .last()
    )
    seq = 1
    if last:
        try:
            seq = int(last.reference_number.split("-")[-1]) + 1
        except (ValueError, IndexError):
            pass
    return f"{prefix}{seq:06d}"


class RevenueRecord(HorillaModel):
    """
    An actual revenue transaction recorded against a RevenueItem.
    """

    reference_number = models.CharField(
        max_length=30, unique=True, editable=False, verbose_name=_("Reference No.")
    )
    item = models.ForeignKey(
        RevenueItem,
        on_delete=models.PROTECT,
        related_name="records",
        verbose_name=_("Revenue Item"),
    )
    department = models.ForeignKey(
        Department,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name=_("Department"),
    )
    amount = models.DecimalField(
        max_digits=15, decimal_places=2, verbose_name=_("Amount (₦)")
    )
    transaction_date = models.DateField(verbose_name=_("Transaction Date"))
    description = models.TextField(blank=True, verbose_name=_("Description"))
    recorded_by = models.ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        editable=False,
        related_name="revenue_records_recorded",
        verbose_name=_("Recorded By"),
    )

    class Meta:
        verbose_name = _("Revenue Record")
        verbose_name_plural = _("Revenue Records")
        ordering = ["-transaction_date", "-created_at"]

    def save(self, *args, **kwargs):
        if not self.reference_number:
            self.reference_number = _generate_rev_reference()
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.reference_number} — ₦{self.amount:,.2f} ({self.item.code})"

    # ------------------------------------------------------------------
    # Horilla URL helpers
    # ------------------------------------------------------------------

    def get_absolute_url(self):
        return reverse_lazy("revenue-record-list")

    def get_update_url(self):
        return reverse_lazy("revenue-record-edit", kwargs={"pk": self.pk})

    def get_delete_url(self):
        return reverse_lazy("revenue-record-delete", kwargs={"pk": self.pk})

    def get_delete_instance(self):
        return self.pk

    def amount_display(self):
        return format_html("₦{:,.2f}", self.amount)

    amount_display.short_description = _("Amount (₦)")
