from __future__ import annotations

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 EmploymentStatus, EmploymentType
from simorgh.core.models import (
    ScopedSoftDeleteManager,
    SoftDeleteModel,
    TenantScopedModel,
    UUIDModel,
    VersionedModel,
)


class Employment(UUIDModel, TenantScopedModel, SoftDeleteModel, VersionedModel):
    """An employee's employment relationship with the organization.

    Separates the person record (Employee) from the employment terms.
    An employee can have multiple Employment records over time (rehires).
    Only one Employment can be active at a time per employee.
    """

    objects = ScopedSoftDeleteManager()

    employee = models.OneToOneField(
        "Employee",
        on_delete=models.CASCADE,
        related_name="employment",
        verbose_name=_("employee"),
    )
    employment_type = models.CharField(
        _("employment type"),
        max_length=20,
        choices=EmploymentType.choices,
        default=EmploymentType.FULL_TIME,
    )
    hire_date = models.DateField(_("hire date"))
    termination_date = models.DateField(_("termination date"), null=True, blank=True)
    probation_end_date = models.DateField(_("probation end date"), null=True, blank=True)
    notice_period_days = models.PositiveSmallIntegerField(
        _("notice period (days)"),
        default=30,
    )
    status = models.CharField(
        _("status"),
        max_length=20,
        choices=EmploymentStatus.choices,
        default=EmploymentStatus.ACTIVE,
    )
    position = models.ForeignKey(
        "Position",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="employments",
        verbose_name=_("position"),
    )
    department = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="employments",
        verbose_name=_("department"),
    )
    job_title = models.ForeignKey(
        "JobTitle",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="employments",
        verbose_name=_("job title"),
    )
    manager = models.ForeignKey(
        "Employee",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="managed_employments",
        verbose_name=_("manager"),
    )
    work_location = models.CharField(_("work location"), max_length=200, blank=True)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_employment"
        verbose_name = _("Employment")
        verbose_name_plural = _("Employments")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["employee"],
                condition=models.Q(status=EmploymentStatus.ACTIVE),
                name="hr_employment_one_active_per_employee",
            )
        ]
        indexes: ClassVar[list[models.Index]] = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=["tenant", "status"], name="hr_employment_tenant_status"),
            models.Index(fields=["tenant", "department"], name="hr_employment_tenant_dept"),
            models.Index(fields=["tenant", "position"], name="hr_employment_tenant_position"),
        ]
        ordering = ["employee__last_name", "employee__first_name"]

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


class ContractType(UUIDModel, TenantScopedModel):
    """Reference data: types of employment contracts."""

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

    code = models.CharField(_("code"), max_length=40)
    name = models.CharField(_("name"), max_length=120)
    is_fixed_term = models.BooleanField(_("fixed term"), default=False)
    default_notice_days = models.PositiveSmallIntegerField(
        _("default notice days"),
        default=30,
    )
    description = models.TextField(_("description"), blank=True)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_contract_type"
        verbose_name = _("Contract Type")
        verbose_name_plural = _("Contract Types")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["tenant", "code"],
                name="hr_contracttype_unique_tenant_code",
            )
        ]
        ordering = ["name"]

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


class EmploymentContract(UUIDModel, TenantScopedModel):
    """A legal employment contract linked to an Employment record."""

    employment = models.ForeignKey(
        Employment,
        on_delete=models.CASCADE,
        related_name="contracts",
        verbose_name=_("employment"),
    )
    contract_type = models.ForeignKey(
        ContractType,
        on_delete=models.PROTECT,
        related_name="contracts",
        verbose_name=_("contract type"),
    )
    start_date = models.DateField(_("start date"))
    end_date = models.DateField(_("end date"), null=True, blank=True)
    signed_date = models.DateField(_("signed date"), null=True, blank=True)
    document_reference = models.CharField(
        _("document reference"),
        max_length=36,
        blank=True,
        help_text=_("UUID of the signed contract in DMS."),
    )
    is_active = models.BooleanField(_("active"), default=True)
    terms = models.JSONField(_("terms"), default=dict, blank=True)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_employment_contract"
        verbose_name = _("Employment Contract")
        verbose_name_plural = _("Employment Contracts")
        ordering = ["-start_date"]

    def __str__(self) -> str:
        return f"{self.employment.employee} — {self.contract_type} ({self.start_date})"
