from __future__ import annotations

from datetime import date as _date, datetime
from typing import ClassVar

from django.db import models
from django.utils.translation import gettext_lazy as _

from simorgh.apps.hr_core.models.choices import AttendanceSource
from simorgh.apps.hr_core.models.employee import Employee
from simorgh.core.models import (
    TenantScopedModel,
    TenantWideScopedManager,
    UUIDModel,
)


class ShiftDefinition(TenantScopedModel):
    """Tenant-wide work shift template.

    ``organization_node`` is NULL — shifts are tenant-wide reference data.
    """

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="shift_definitions",
        verbose_name=_("tenant"),
    )
    organization_node = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
        verbose_name=_("organization node"),
    )

    objects = TenantWideScopedManager()

    name = models.CharField(_("name"), max_length=120)
    start_time = models.TimeField(_("start time"))
    end_time = models.TimeField(_("end time"))
    work_days = models.JSONField(
        _("work days"), default=list,
        help_text=_("List of weekday ints (0=Mon … 6=Sun)."),
    )
    break_minutes = models.PositiveSmallIntegerField(
        _("break minutes"), default=0,
        help_text=_("Scheduled break duration deducted from work hours."),
    )

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_shiftdefinition"
        verbose_name = _("Shift Definition")
        verbose_name_plural = _("Shift Definitions")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["tenant", "name"],
                name="hr_shiftdef_unique_tenant_name",
            )
        ]
        ordering = ["name"]

    @property
    def expected_work_minutes(self) -> int:
        base = _date(2000, 1, 1)
        start_dt = datetime.combine(base, self.start_time)
        end_dt = datetime.combine(base, self.end_time)
        diff = (end_dt - start_dt).seconds // 60
        return max(0, diff - self.break_minutes)

    def __str__(self) -> str:
        return self.name


class EmployeeShift(TenantScopedModel):
    """Assignment of a ShiftDefinition to an employee for a date range.

    ``organization_node`` is populated from the employee at creation time.
    """

    employee = models.ForeignKey(
        Employee, on_delete=models.CASCADE, related_name="shift_assignments",
        verbose_name=_("employee"),
    )
    shift = models.ForeignKey(
        ShiftDefinition, on_delete=models.PROTECT, related_name="employee_assignments",
        verbose_name=_("shift"),
    )
    effective_from = models.DateField(_("effective from"))
    effective_to = models.DateField(_("effective to"), null=True, blank=True)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_employeeshift"
        verbose_name = _("Employee Shift")
        verbose_name_plural = _("Employee Shifts")
        ordering = ["-effective_from"]
        indexes: ClassVar[list[models.Index]] = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=["employee", "effective_from"], name="hr_empshift_employee_from"),
        ]

    def __str__(self) -> str:
        return f"{self.employee} → {self.shift} from {self.effective_from}"


class AttendanceRecord(UUIDModel, TenantScopedModel):
    """Daily attendance record per employee.

    ``organization_node`` is populated from the employee at creation time.
    """

    tenant = models.ForeignKey(
        "tenants.Tenant",
        on_delete=models.CASCADE,
        related_name="attendance_records",
        verbose_name=_("tenant"),
    )
    organization_node = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.PROTECT,
        related_name="+",
        verbose_name=_("organization node"),
    )

    employee = models.ForeignKey(
        Employee, on_delete=models.CASCADE, related_name="attendance_records",
        verbose_name=_("employee"),
    )
    date = models.DateField(_("date"))
    check_in = models.TimeField(_("check-in time"), null=True, blank=True)
    check_out = models.TimeField(_("check-out time"), null=True, blank=True)
    work_minutes = models.PositiveIntegerField(
        _("work minutes"), null=True, blank=True,
        help_text=_("Computed: (check_out - check_in) - break_minutes."),
    )
    overtime_minutes = models.PositiveIntegerField(_("overtime minutes"), default=0)
    source = models.CharField(
        _("source"), max_length=20, choices=AttendanceSource.choices,
        default=AttendanceSource.MANUAL,
    )
    note = models.TextField(_("note"), blank=True, default="")
    is_finalized = models.BooleanField(
        _("finalized"), default=False,
        help_text=_("Finalized records cannot be manually edited."),
    )

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_attendancerecord"
        verbose_name = _("Attendance Record")
        verbose_name_plural = _("Attendance Records")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["tenant", "employee", "date"],
                name="hr_attendance_unique_tenant_employee_date",
            )
        ]
        indexes: ClassVar[list[models.Index]] = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=["tenant", "employee", "date"], name="hr_att_tenant_emp_date"),
            models.Index(fields=["tenant", "date", "is_finalized"], name="hr_att_tenant_date_fin"),
        ]
        ordering = ["-date"]

    def __str__(self) -> str:
        return f"{self.employee} @ {self.date}"
