from django.conf import settings
from django.db import models

from apps.core.models import ActiveModel, TimeStampedModel
from apps.employees.models import Employee
from apps.organization.models import Branch, Company


class AttendanceDevice(ActiveModel):
    company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name="attendance_devices")
    branch = models.ForeignKey(Branch, on_delete=models.PROTECT, related_name="attendance_devices")
    name = models.CharField(max_length=120)
    vendor = models.CharField(max_length=80, default="ZKTeco")
    model = models.CharField(max_length=80, default="FO-M1")
    serial_number = models.CharField(max_length=100, blank=True)
    connection_mode = models.CharField(max_length=30, default="TCP/IP")
    ip_address = models.GenericIPAddressField(null=True, blank=True)
    port = models.PositiveIntegerField(default=4370)
    comm_key = models.CharField(max_length=100, blank=True)
    adms_url = models.URLField(blank=True)
    last_sync_at = models.DateTimeField(null=True, blank=True)
    last_sync_status = models.CharField(max_length=120, blank=True)

    def __str__(self):
        return f"{self.vendor} {self.model} - {self.name}"


class DeviceEmployeeMap(TimeStampedModel):
    device = models.ForeignKey(AttendanceDevice, on_delete=models.CASCADE, related_name="employee_maps")
    employee = models.ForeignKey(Employee, on_delete=models.PROTECT, related_name="device_maps")
    device_user_id = models.CharField(max_length=50)
    is_active = models.BooleanField(default=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=["device", "device_user_id"], name="uq_device_user_id"),
            models.UniqueConstraint(fields=["device", "employee"], name="uq_device_employee"),
        ]


class PunchLog(TimeStampedModel):
    device = models.ForeignKey(AttendanceDevice, on_delete=models.PROTECT, related_name="punches")
    device_user_id = models.CharField(max_length=50)
    employee = models.ForeignKey(Employee, on_delete=models.PROTECT, null=True, blank=True, related_name="punches")
    punch_time = models.DateTimeField(db_index=True)
    punch_type = models.CharField(max_length=30, blank=True)
    source = models.CharField(max_length=30, default="DEVICE")
    raw_payload = models.JSONField(default=dict, blank=True)

    class Meta:
        constraints = [models.UniqueConstraint(fields=["device", "device_user_id", "punch_time"], name="uq_device_punch")]


class AttendanceRecord(TimeStampedModel):
    class Status(models.TextChoices):
        PRESENT = "PRESENT", "Present"
        ABSENT = "ABSENT", "Absent"
        LATE = "LATE", "Late"
        LEAVE = "LEAVE", "Leave"
        HOLIDAY = "HOLIDAY", "Holiday"
        WEEKLY_OFF = "WEEKLY_OFF", "Weekly Off"
        HALF_DAY = "HALF_DAY", "Half Day"
        WFH = "WFH", "Work From Home"
        OFFICIAL_DUTY = "OFFICIAL_DUTY", "Official Duty"

    employee = models.ForeignKey(Employee, on_delete=models.PROTECT, related_name="attendance_records")
    work_date = models.DateField(db_index=True)
    check_in = models.DateTimeField(null=True, blank=True)
    check_out = models.DateTimeField(null=True, blank=True)
    worked_minutes = models.PositiveIntegerField(default=0)
    late_minutes = models.PositiveIntegerField(default=0)
    early_exit_minutes = models.PositiveIntegerField(default=0)
    extra_minutes = models.PositiveIntegerField(default=0)
    payable_ot_minutes = models.PositiveIntegerField(default=0)
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.PRESENT)
    finalized = models.BooleanField(default=False)
    remarks = models.CharField(max_length=255, blank=True)

    class Meta:
        constraints = [models.UniqueConstraint(fields=["employee", "work_date"], name="uq_employee_attendance_date")]
        indexes = [models.Index(fields=["work_date", "finalized"])]


class AttendancePeriod(TimeStampedModel):
    class Status(models.TextChoices):
        OPEN = "OPEN", "Open"
        FINALIZED = "FINALIZED", "Finalized"

    company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name="attendance_periods")
    year = models.PositiveIntegerField()
    month = models.PositiveIntegerField()
    status = models.CharField(max_length=20, choices=Status.choices, default=Status.OPEN)
    finalized_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=True, blank=True, related_name="finalized_attendance_periods")
    finalized_at = models.DateTimeField(null=True, blank=True)
    reopened_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=True, blank=True, related_name="reopened_attendance_periods")
    reopened_at = models.DateTimeField(null=True, blank=True)
    reopen_reason = models.TextField(blank=True)

    class Meta:
        constraints = [models.UniqueConstraint(fields=["company", "year", "month"], name="uq_attendance_company_month")]
        ordering = ["-year", "-month", "company__name"]


class AttendanceDayLedger(TimeStampedModel):
    """Authoritative day-level source consumed by payroll.

    The ledger prevents paid/unpaid leave and absence being independently counted
    by different payroll queries. Exactly one deduction fraction exists per employee/day.
    """

    company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name="attendance_day_ledgers")
    employee = models.ForeignKey(Employee, on_delete=models.PROTECT, related_name="attendance_day_ledgers")
    work_date = models.DateField(db_index=True)
    attendance_record = models.ForeignKey(AttendanceRecord, on_delete=models.PROTECT, null=True, blank=True, related_name="day_ledgers")
    leave_request = models.ForeignKey("leave.LeaveRequest", on_delete=models.PROTECT, null=True, blank=True, related_name="attendance_day_ledgers")
    day_status = models.CharField(max_length=20, choices=AttendanceRecord.Status.choices)
    source = models.CharField(max_length=40, default="RECONCILIATION")
    deduction_fraction = models.DecimalField(max_digits=5, decimal_places=2, default=0)
    payable_fraction = models.DecimalField(max_digits=5, decimal_places=2, default=1)
    finalized = models.BooleanField(default=False)
    policy_snapshot = models.JSONField(default=dict, blank=True)
    notes = models.CharField(max_length=255, blank=True)

    class Meta:
        constraints = [models.UniqueConstraint(fields=["employee", "work_date"], name="uq_attendance_day_ledger")]
        indexes = [models.Index(fields=["company", "work_date", "finalized"])]


class AttendanceAdjustment(TimeStampedModel):
    attendance = models.ForeignKey(AttendanceRecord, on_delete=models.PROTECT, related_name="adjustments")
    requested_by_user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="attendance_adjustment_requests"
    )
    requester_employee = models.ForeignKey(
        Employee, on_delete=models.SET_NULL, null=True, blank=True, related_name="submitted_attendance_adjustments"
    )
    # True only when the affected employee submitted the request for themself.
    requested_by_employee = models.BooleanField(default=False)
    reason = models.TextField()
    old_values = models.JSONField(default=dict)
    new_values = models.JSONField(default=dict)
    status = models.CharField(max_length=20, default="PENDING")


class OvertimeEntry(TimeStampedModel):
    employee = models.ForeignKey(Employee, on_delete=models.PROTECT, related_name="overtime_entries")
    work_date = models.DateField()
    requested_minutes = models.PositiveIntegerField(default=0)
    approved_minutes = models.PositiveIntegerField(default=0)
    rate_multiplier = models.DecimalField(max_digits=6, decimal_places=2, default=1.00)
    status = models.CharField(max_length=20, default="PENDING")
    reason = models.TextField(blank=True)

    class Meta:
        constraints = [models.UniqueConstraint(fields=["employee", "work_date"], name="uq_employee_ot_date")]
