from django.contrib.auth.hashers import check_password, make_password
from django.contrib.auth.models import AbstractUser
from django.db import models


class Permission(models.Model):
    """A single grantable capability. Two kinds, distinguished by `kind`:

    ACTION permissions gate an operation (e.g. 'purchases.manage' — full access to
    the Purchases endpoints). COLUMN permissions gate visibility of one sensitive
    field on a list/export a role can otherwise already see (e.g.
    'columns.spare_parts.buying_price' — see the cost column on the Spare Parts
    list even though the part itself is visible to every authenticated user).

    Seeded by a data migration; codename is the stable string checked in code
    (`user.has_perm_code(...)`) and referenced by the frontend for column gating.
    """

    class Kind(models.TextChoices):
        ACTION = 'ACTION', 'Action'
        COLUMN = 'COLUMN', 'Column visibility'

    codename = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=150)
    category = models.CharField(max_length=50, help_text='Groups permissions in the role editor, e.g. "Inventory".')
    kind = models.CharField(max_length=10, choices=Kind.choices, default=Kind.ACTION)

    class Meta:
        ordering = ['category', 'kind', 'name']

    def __str__(self):
        return self.name


class Role(models.Model):
    """A named, admin-editable set of permissions. Users are bound to exactly one role.

    Two system roles (Manager, Seller) are seeded to match the app's pre-RBAC
    behavior exactly; `is_system` just protects them from deletion — they're
    editable like any other role once RBAC is in place.
    """

    name = models.CharField(max_length=50, unique=True)
    description = models.TextField(blank=True)
    is_system = models.BooleanField(default=False)
    permissions = models.ManyToManyField(Permission, related_name='roles', blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name


class User(AbstractUser):
    role = models.ForeignKey(
        Role,
        on_delete=models.PROTECT,
        related_name='users',
        null=True,
        blank=True,
    )

    # Short PIN a manager enters to approve a below-minimum-price sale at the
    # point of sale, without exposing their real login password to a seller.
    approval_pin = models.CharField(max_length=128, blank=True)
    approval_pin_failed_attempts = models.PositiveSmallIntegerField(default=0)
    approval_pin_locked_until = models.DateTimeField(null=True, blank=True)

    @property
    def is_manager(self):
        """True for the built-in Manager role. Kept for call sites that mean
        "the owner-level system role" specifically, not a general permission check."""
        return bool(self.role_id) and self.role.is_system and self.role.name == 'Manager'

    def has_perm_code(self, codename):
        if not self.role_id:
            return False
        return self.role.permissions.filter(codename=codename).exists()

    @property
    def has_approval_pin(self):
        return bool(self.approval_pin)

    def set_approval_pin(self, raw_pin):
        self.approval_pin = make_password(raw_pin)

    def check_approval_pin(self, raw_pin):
        return bool(self.approval_pin) and check_password(raw_pin, self.approval_pin)
