"""Django ORM models — the actual persisted tables.

These live in the infrastructure layer per the project's Clean
Architecture convention: domain/ holds pure entities & interfaces,
infrastructure/ holds the Django-specific persistence implementation.
"""
from __future__ import annotations

from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.db import models


class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, personnel_code, password=None, **extra_fields):
        if not personnel_code:
            raise ValueError("personnel_code is required")
        extra_fields.setdefault("username", str(personnel_code).strip().lower())
        user = self.model(personnel_code=personnel_code, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, personnel_code, password=None, **extra_fields):
        extra_fields.setdefault("is_admin", False)
        return self._create_user(personnel_code, password, **extra_fields)

    def create_superuser(self, personnel_code, password=None, **extra_fields):
        extra_fields.setdefault("is_admin", True)
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        return self._create_user(personnel_code, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
    """Custom user model (the authentication account).

    A person signs in with `username` or a verified `mobile`. `personnel_code` is the legacy
    identifier kept for the existing timesheet API; org-specific employee codes live on
    OrganizationMembership.
    """

    personnel_code = models.CharField(max_length=50, unique=True, db_index=True)
    username = models.CharField(max_length=50, unique=True, null=True, blank=True)
    mobile = models.CharField(max_length=11, unique=True, null=True, blank=True)
    mobile_verified_at = models.DateTimeField(null=True, blank=True)
    name = models.CharField(max_length=255, null=True, blank=True)
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    date_joined = models.DateTimeField(auto_now_add=True)

    objects = UserManager()

    USERNAME_FIELD = "personnel_code"
    REQUIRED_FIELDS = []

    class Meta:
        db_table = "users"

    def __str__(self):
        return f"{self.personnel_code} ({self.name or 'no name'})"

    @property
    def has_name(self) -> bool:
        return bool(self.name)


class Attendance(models.Model):
    organization = models.ForeignKey(
        "organizations.Organization", on_delete=models.PROTECT, related_name="+"
    )
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="attendances")
    date = models.DateField()
    check_in = models.TimeField(null=True, blank=True)
    check_out = models.TimeField(null=True, blank=True)
    is_default_checkout = models.BooleanField(default=False)
    tasks_note = models.TextField(null=True, blank=True)

    class Meta:
        db_table = "attendances"
        constraints = [
            models.UniqueConstraint(
                fields=["organization", "user", "date"], name="unique_org_user_date_attendance"
            )
        ]
        indexes = [models.Index(fields=["organization", "user", "date"])]

    def __str__(self):
        return f"{self.user.personnel_code} - {self.date}"

    @property
    def worked_minutes(self):
        if not self.check_in or not self.check_out:
            return None
        import datetime

        today = datetime.date.today()
        dt_in = datetime.datetime.combine(today, self.check_in)
        dt_out = datetime.datetime.combine(today, self.check_out)
        return abs(int((dt_out - dt_in).total_seconds() // 60))

    @property
    def worked_hours_formatted(self):
        minutes = self.worked_minutes
        if minutes is None:
            return None
        from domain.value_objects import format_minutes_as_hm

        return format_minutes_as_hm(minutes)


class Holiday(models.Model):
    organization = models.ForeignKey(
        "organizations.Organization", on_delete=models.PROTECT, related_name="+"
    )
    date = models.DateField()
    title = models.CharField(max_length=255)
    description = models.TextField(null=True, blank=True)

    class Meta:
        db_table = "holidays"
        ordering = ["date"]
        constraints = [models.UniqueConstraint(fields=["organization", "date"], name="unique_org_holiday_date")]

    def __str__(self):
        return f"{self.date} - {self.title}"


class WorkSchedule(models.Model):
    """Admin-defined standard check-in/check-out time used to compute
    lateness and overtime on the monthly report. `date=None` is the global
    default; a row with a specific `date` overrides the default for that
    single day only."""

    organization = models.ForeignKey(
        "organizations.Organization", on_delete=models.PROTECT, related_name="+"
    )
    date = models.DateField(null=True, blank=True)
    standard_check_in = models.TimeField()
    standard_check_out = models.TimeField()

    class Meta:
        db_table = "work_schedules"
        ordering = ["date"]
        constraints = [models.UniqueConstraint(fields=["organization", "date"], name="unique_org_schedule_date")]

    def __str__(self):
        label = str(self.date) if self.date else "default"
        return f"{label}: {self.standard_check_in}-{self.standard_check_out}"


class LeaveRequest(models.Model):
    class LeaveType(models.TextChoices):
        DAILY = "daily", "Daily"
        HOURLY = "hourly", "Hourly"

    class LeaveCategory(models.TextChoices):
        ANNUAL = "annual", "Annual"
        SICK = "sick", "Sick"
        UNPAID = "unpaid", "Unpaid"

    class Status(models.TextChoices):
        PENDING = "pending", "Pending"
        APPROVED = "approved", "Approved"
        REJECTED = "rejected", "Rejected"

    organization = models.ForeignKey(
        "organizations.Organization", on_delete=models.PROTECT, related_name="+"
    )
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="leave_requests")
    date = models.DateField()
    type = models.CharField(max_length=10, choices=LeaveType.choices)
    start_time = models.TimeField(null=True, blank=True)
    end_time = models.TimeField(null=True, blank=True)
    category = models.CharField(max_length=10, choices=LeaveCategory.choices)
    reason = models.TextField(null=True, blank=True)
    status = models.CharField(max_length=10, choices=Status.choices, default=Status.PENDING)
    reviewed_by = models.ForeignKey(
        User, on_delete=models.SET_NULL, null=True, blank=True, related_name="reviewed_leave_requests"
    )
    reviewed_at = models.DateTimeField(null=True, blank=True)
    review_note = models.TextField(null=True, blank=True)  # the reviewer's reason for approving / rejecting
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = "leave_requests"
        ordering = ["-date", "-id"]
        indexes = [models.Index(fields=["organization", "user", "date"])]

    def __str__(self):
        return f"{self.user.personnel_code} - {self.date} - {self.status}"
