from __future__ import annotations

from typing import ClassVar

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

from simorgh.apps.hr_core.models.choices import DocumentType, EmployeeStatus, EmploymentType, Gender
from simorgh.core.models import (
    ScopedSoftDeleteManager,
    SoftDeleteModel,
    TenantScopedModel,
    UUIDModel,
    VersionedModel,
)


class Employee(UUIDModel, TenantScopedModel, SoftDeleteModel, VersionedModel):
    objects = ScopedSoftDeleteManager()

    soft_cascade: ClassVar[tuple[str, ...]] = ("documents", "bank_accounts")

    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="employee_profile",
        verbose_name=_("user account"),
    )
    employee_no = models.CharField(_("employee number"), max_length=30, blank=True)
    first_name = models.CharField(_("first name"), max_length=80)
    last_name = models.CharField(_("last name"), max_length=80)
    display_name = models.CharField(_("display name"), max_length=160, blank=True)
    national_id = models.CharField(
        _("national ID"),
        max_length=512,
        blank=True,
        help_text=_("Stored encrypted. Requires hr.employee.sensitive.view to read."),
    )
    birth_date = models.DateField(_("birth date"), null=True, blank=True)
    gender = models.CharField(
        _("gender"),
        max_length=20,
        choices=Gender.choices,
        blank=True,
    )
    mobile = models.CharField(_("mobile"), max_length=30, blank=True)
    personal_email = models.EmailField(_("personal email"), blank=True)
    work_email = models.EmailField(_("work email"), blank=True)

    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)
    status = models.CharField(
        _("status"),
        max_length=20,
        choices=EmployeeStatus.choices,
        default=EmployeeStatus.ACTIVE,
    )
    department = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="employees",
        verbose_name=_("department"),
    )
    manager = models.ForeignKey(
        "self",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="direct_reports",
        verbose_name=_("manager"),
    )
    job_title = models.ForeignKey(
        "JobTitle",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="employees",
        verbose_name=_("job title"),
    )
    work_location = models.CharField(_("work location"), max_length=200, blank=True)
    extra_data = models.JSONField(
        _("extra data"),
        default=dict,
        blank=True,
        help_text=_("Arbitrary extensible key-value data for custom fields."),
    )

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_employee"
        verbose_name = _("Employee")
        verbose_name_plural = _("Employees")
        indexes: ClassVar[list[models.Index]] = [
            *TenantScopedModel.Meta.indexes,
            models.Index(fields=["tenant", "status"], name="hr_employee_tenant_status"),
            models.Index(fields=["tenant", "department"], name="hr_employee_tenant_dept"),
        ]
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["tenant", "employee_no"],
                condition=models.Q(employee_no__gt=""),
                name="hr_employee_unique_tenant_employee_no",
            ),
        ]
        ordering = ["last_name", "first_name"]

    def __str__(self) -> str:
        return self.display_name or f"{self.first_name} {self.last_name}"

    def get_full_name(self) -> str:
        return f"{self.first_name} {self.last_name}".strip()

    AI_HINTS: ClassVar[dict[str, str]] = {
        "employee_no": "Unique identifier assigned to each employee within the tenant.",
        "first_name": "Employee's given (first) name.",
        "last_name": "Employee's family (last) name.",
        "display_name": "Preferred display name; falls back to first + last name.",
        "national_id": "Government-issued national ID number (encrypted at rest).",
        "birth_date": "Employee's date of birth; used for age calculation and benefits.",
        "gender": "Employee's self-reported gender identity.",
        "employment_type": (
            "Contract type: full_time, part_time, contract, or intern. "
            "Affects benefits eligibility and leave entitlement."
        ),
        "hire_date": "The date the employee officially joined the organisation.",
        "termination_date": "The date the employee left (terminated or resigned). Null if still employed.",
        "status": (
            "Current employment status: active (working), on_leave (approved absence), "
            "terminated (dismissed), or resigned (voluntary departure)."
        ),
        "department": "The organisational unit (department) the employee belongs to.",
        "manager": "Direct reporting manager; null for top-level employees.",
        "job_title": "The employee's role/title within the organisation.",
        "work_location": "Office, branch, or remote location where the employee works.",
    }


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

    employee = models.ForeignKey(
        Employee,
        on_delete=models.CASCADE,
        related_name="documents",
        verbose_name=_("employee"),
    )
    doc_type = models.CharField(
        _("document type"),
        max_length=20,
        choices=DocumentType.choices,
        default=DocumentType.OTHER,
    )
    dms_file_id = models.CharField(
        _("DMS file ID"),
        max_length=36,
        blank=True,
        help_text=_("UUID of the DMS FileRecord. Loose reference; no FK constraint."),
    )
    title = models.CharField(_("title"), max_length=200, blank=True)
    expiry_date = models.DateField(_("expiry date"), null=True, blank=True)
    is_verified = models.BooleanField(_("verified"), default=False)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_employeedocument"
        verbose_name = _("Employee Document")
        verbose_name_plural = _("Employee Documents")
        ordering = ["-created_at"]

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


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

    employee = models.ForeignKey(
        Employee,
        on_delete=models.CASCADE,
        related_name="bank_accounts",
        verbose_name=_("employee"),
    )
    bank_name = models.CharField(_("bank name"), max_length=120)
    account_no = models.CharField(
        _("account number"),
        max_length=512,
        help_text=_("Stored encrypted."),
    )
    iban = models.CharField(
        _("IBAN"),
        max_length=512,
        blank=True,
        help_text=_("Stored encrypted."),
    )
    is_default = models.BooleanField(_("default"), default=False)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_employeebankaccount"
        verbose_name = _("Bank Account")
        verbose_name_plural = _("Bank Accounts")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["employee", "is_default"],
                condition=models.Q(is_default=True),
                name="hr_bank_account_one_default_per_employee",
            )
        ]
        ordering = ["-is_default", "bank_name"]

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


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

    title = models.CharField(_("title"), max_length=120)
    level = models.PositiveSmallIntegerField(
        _("level"),
        default=1,
        help_text=_("Seniority level 1 (junior) – 10 (executive)."),
    )
    department = models.ForeignKey(
        "organizations.OrganizationNode",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="job_titles",
        verbose_name=_("department"),
    )

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_jobtitle"
        verbose_name = _("Job Title")
        verbose_name_plural = _("Job Titles")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["tenant", "title"],
                name="hr_jobtitle_unique_tenant_title",
            )
        ]
        ordering = ["title"]

    job_family = models.ForeignKey(
        "JobFamily",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="job_titles",
        verbose_name=_("job family"),
    )

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


class EmergencyContact(UUIDModel, TenantScopedModel):
    """Emergency contact for an employee."""

    employee = models.ForeignKey(
        Employee,
        on_delete=models.CASCADE,
        related_name="emergency_contacts",
        verbose_name=_("employee"),
    )
    name = models.CharField(_("name"), max_length=160)
    relationship = models.CharField(_("relationship"), max_length=80)
    primary_phone = models.CharField(_("primary phone"), max_length=30)
    secondary_phone = models.CharField(_("secondary phone"), max_length=30, blank=True)
    email = models.EmailField(_("email"), blank=True)
    address = models.TextField(_("address"), blank=True)
    is_primary = models.BooleanField(_("primary contact"), default=False)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_emergency_contact"
        verbose_name = _("Emergency Contact")
        verbose_name_plural = _("Emergency Contacts")
        constraints: ClassVar[list] = [
            models.UniqueConstraint(
                fields=["employee", "is_primary"],
                condition=models.Q(is_primary=True),
                name="hr_emergency_one_primary_per_employee",
            )
        ]
        ordering = ["-is_primary", "name"]

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


class Dependent(UUIDModel, TenantScopedModel):
    """Employee's dependent (spouse, child) for benefits eligibility."""

    employee = models.ForeignKey(
        Employee,
        on_delete=models.CASCADE,
        related_name="dependents",
        verbose_name=_("employee"),
    )
    first_name = models.CharField(_("first name"), max_length=80)
    last_name = models.CharField(_("last name"), max_length=80)
    relationship = models.CharField(_("relationship"), max_length=40)
    birth_date = models.DateField(_("birth date"))
    national_id = models.CharField(
        _("national ID"),
        max_length=512,
        blank=True,
        help_text=_("Stored encrypted."),
    )
    is_beneficiary = models.BooleanField(_("beneficiary"), default=False)

    class Meta(TenantScopedModel.Meta):
        db_table = "hr_dependent"
        verbose_name = _("Dependent")
        verbose_name_plural = _("Dependents")
        ordering = ["last_name", "first_name"]

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