from decimal import Decimal

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import IntegrityError, models, transaction


def parse_day_list(value):
    """'30, 7,3,0' -> [30, 7, 3, 0]. Raises ValueError on bad input."""
    days = [int(part.strip()) for part in str(value).split(",") if part.strip()]
    if any(d < 0 or d > 3650 for d in days):
        raise ValueError("out of range")
    return days


def validate_day_list(value):
    try:
        parse_day_list(value)
    except ValueError:
        raise ValidationError("أدخل أرقامًا صحيحة غير سالبة مفصولة بفواصل، مثل: 30,7,3,0")


class CompanySettings(models.Model):
    """Singleton (pk=1). Invoices snapshot these values at finalization, so later edits never alter issued invoices."""

    name_ar = models.CharField("اسم الشركة (عربي)", max_length=200, default="اسم الشركة")
    name_en = models.CharField("اسم الشركة (إنجليزي)", max_length=200, blank=True)
    address = models.TextField("العنوان", blank=True)
    phone = models.CharField("الهاتف", max_length=50, blank=True)
    email = models.EmailField("البريد الإلكتروني", blank=True)
    website = models.CharField("الموقع الإلكتروني", max_length=200, blank=True)
    tax_number = models.CharField("الرقم الضريبي", max_length=50, blank=True)

    tax_enabled = models.BooleanField("تفعيل الضريبة على الفواتير", default=False)
    default_tax_rate = models.DecimalField(
        "نسبة الضريبة الافتراضية (%)", max_digits=5, decimal_places=2, default=Decimal("0.00"),
        validators=[MinValueValidator(Decimal("0")), MaxValueValidator(Decimal("100"))],
        help_text="لا يوجد افتراض قانوني؛ أدخل النسبة المعتمدة لديك فقط عند الحاجة.",
    )
    payment_terms_days = models.PositiveSmallIntegerField("مدة استحقاق الفاتورة (أيام)", default=14)
    invoice_footer = models.TextField("تذييل الفاتورة", blank=True)

    reminder_days_before = models.CharField(
        "تنبيهات التجديد قبل الانتهاء (أيام)", max_length=100, default="30,7,3,0", validators=[validate_day_list],
    )
    reminder_days_after = models.CharField(
        "تنبيهات التجديد بعد الانتهاء (أيام)", max_length=100, default="3,7", validators=[validate_day_list],
    )
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "إعدادات الشركة"
        verbose_name_plural = "إعدادات الشركة"

    def __str__(self):
        return self.name_ar

    def save(self, *args, **kwargs):
        self.pk = 1
        super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        raise models.ProtectedError("لا يمكن حذف إعدادات الشركة.", {self})

    @classmethod
    def load(cls):
        obj, _ = cls.objects.get_or_create(pk=1)
        return obj

    @property
    def reminder_days_before_list(self):
        return parse_day_list(self.reminder_days_before)

    @property
    def reminder_days_after_list(self):
        return parse_day_list(self.reminder_days_after)


class NumberSequence(models.Model):
    """Concurrency-safe counters (e.g. key='invoice-2026'). Always call NumberSequence.next() inside the caller's
    transaction so a rolled-back invoice does not burn a number and the row lock is held until commit."""

    key = models.CharField(max_length=60, unique=True)
    last_value = models.PositiveBigIntegerField(default=0)

    class Meta:
        verbose_name = "تسلسل ترقيم"
        verbose_name_plural = "تسلسلات الترقيم"

    def __str__(self):
        return f"{self.key}: {self.last_value}"

    @classmethod
    def next(cls, key):
        with transaction.atomic():
            try:
                with transaction.atomic():  # savepoint: a lost create race must not poison the outer transaction
                    cls.objects.get_or_create(key=key)
            except IntegrityError:
                pass
            seq = cls.objects.select_for_update().get(key=key)
            seq.last_value += 1
            seq.save(update_fields=["last_value"])
            return seq.last_value


class ActivityLog(models.Model):
    """Append-only audit trail. Rows cannot be edited or deleted through the ORM instance API."""

    LOGIN, LOGOUT, LOGIN_FAILED = "login", "logout", "login_failed"
    CREATE, UPDATE, DELETE, STATUS = "create", "update", "delete", "status"
    FINANCIAL, PERMISSION, SETTINGS, EXPORT, OTHER = "financial", "permission", "settings", "export", "other"
    ACTION_CHOICES = [
        (LOGIN, "تسجيل دخول"), (LOGOUT, "تسجيل خروج"), (LOGIN_FAILED, "محاولة دخول فاشلة"),
        (CREATE, "إنشاء"), (UPDATE, "تعديل"), (DELETE, "حذف"), (STATUS, "تغيير حالة"),
        (FINANCIAL, "عملية مالية"), (PERMISSION, "صلاحيات"), (SETTINGS, "إعدادات"),
        (EXPORT, "تصدير"), (OTHER, "أخرى"),
    ]

    user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name="+")
    username = models.CharField("المستخدم", max_length=150, blank=True)  # snapshot survives user deletion
    action = models.CharField("الإجراء", max_length=20, choices=ACTION_CHOICES)
    object_type = models.CharField("نوع السجل", max_length=100, blank=True)
    object_id = models.CharField(max_length=64, blank=True)
    object_repr = models.CharField("السجل", max_length=255, blank=True)
    details = models.JSONField("التفاصيل", default=dict, blank=True)
    ip_address = models.GenericIPAddressField("عنوان IP", null=True, blank=True)
    created_at = models.DateTimeField("الوقت", auto_now_add=True)

    class Meta:
        verbose_name = "سجل نشاط"
        verbose_name_plural = "سجل النشاط"
        ordering = ["-created_at", "-id"]
        indexes = [
            models.Index(fields=["created_at"]),
            models.Index(fields=["object_type", "object_id"]),
            models.Index(fields=["action"]),
        ]

    def __str__(self):
        return f"{self.created_at:%Y-%m-%d %H:%M} {self.username} {self.action} {self.object_repr}"

    def save(self, *args, **kwargs):
        if self.pk is not None:
            raise PermissionError("سجل النشاط للقراءة فقط.")
        super().save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        raise PermissionError("سجل النشاط لا يمكن حذفه.")

    @classmethod
    def record(cls, action, user=None, obj=None, details=None, request=None, object_type="", object_repr="", username=""):
        if user is not None and not getattr(user, "is_authenticated", False):
            user = None
        ip = None
        if request is not None:
            ip = request.META.get("REMOTE_ADDR") or None
        if obj is not None:
            object_type = object_type or obj._meta.label
            object_repr = object_repr or str(obj)
            object_id = str(obj.pk) if obj.pk is not None else ""
        else:
            object_id = ""
        return cls.objects.create(
            user=user, username=username or (user.get_username() if user else ""), action=action,
            object_type=object_type, object_id=object_id, object_repr=object_repr[:255],
            details=details or {}, ip_address=ip,
        )


class ReportAccess(models.Model):
    """Permission holder only (no table). Gives reports/exports their own permissions."""

    class Meta:
        managed = False
        default_permissions = ()
        permissions = [
            ("view_financial_reports", "عرض التقارير المالية"),
            ("view_operational_reports", "عرض تقارير التشغيل والتجديدات"),
            ("export_data", "تصدير البيانات (CSV)"),
        ]
