"""Phase 5.9 — HCM & Payroll domain models."""
from __future__ import annotations

import uuid
from datetime import date
from decimal import Decimal

from sqlalchemy import Boolean, Date, Index, Integer, Numeric, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column

from app.core.database import Base
from app.models.base import TenantMixin, TimestampMixin, UUIDPrimaryKeyMixin
from app.models.types import EmploymentStatus, GUID, PayrollPeriodStatus, PayrollStatus


class Department(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "departments"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    parent_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    cost_center_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_department_tenant_code"),)


class Position(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "positions"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_position_tenant_code"),)


class Employee(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "employees"

    employee_code: Mapped[str] = mapped_column(String(50), nullable=False)
    first_name: Mapped[str] = mapped_column(String(100), nullable=False)
    last_name: Mapped[str] = mapped_column(String(100), nullable=False)
    department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    position_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    cost_center_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    hire_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    employment_status: Mapped[EmploymentStatus] = mapped_column(
        default=EmploymentStatus.ACTIVE, nullable=False
    )
    base_salary: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)

    __table_args__ = (
        UniqueConstraint("tenant_id", "employee_code", name="uq_employee_tenant_code"),
        Index("ix_employees_tenant_dept", "tenant_id", "department_id"),
    )


class EmploymentContract(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "employment_contracts"

    employee_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    contract_type: Mapped[str] = mapped_column(String(50), nullable=False)
    start_date: Mapped[date] = mapped_column(Date, nullable=False)
    end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    base_salary: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)


class SalaryComponent(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "salary_components"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    component_type: Mapped[str] = mapped_column(String(20), nullable=False)
    is_taxable: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_salary_component_tenant_code"),)


class PayrollPeriod(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payroll_periods"

    name: Mapped[str] = mapped_column(String(100), nullable=False)
    start_date: Mapped[date] = mapped_column(Date, nullable=False)
    end_date: Mapped[date] = mapped_column(Date, nullable=False)
    status: Mapped[PayrollPeriodStatus] = mapped_column(
        default=PayrollPeriodStatus.OPEN, nullable=False
    )
    is_current: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (UniqueConstraint("tenant_id", "name", name="uq_payroll_period_tenant_name"),)


class Payroll(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payrolls"

    payroll_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    employee_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    gross_salary: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    total_deductions: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
    total_benefits: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
    net_salary: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    employer_cost: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
    status: Mapped[PayrollStatus] = mapped_column(default=PayrollStatus.DRAFT, nullable=False)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)

    __table_args__ = (
        Index("ix_payroll_period_employee", "tenant_id", "payroll_period_id", "employee_id"),
    )


class PayrollItem(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payroll_items"

    payroll_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    component_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    item_type: Mapped[str] = mapped_column(String(20), nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    description: Mapped[str | None] = mapped_column(String(255), nullable=True)


class Benefit(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "benefits"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), default=Decimal("0"), nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (UniqueConstraint("tenant_id", "code", name="uq_benefit_tenant_code"),)


class Allowance(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "allowances"

    employee_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    component_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    effective_date: Mapped[date] = mapped_column(Date, nullable=False)


class Deduction(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "deductions"

    employee_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    component_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    effective_date: Mapped[date] = mapped_column(Date, nullable=False)


class EmployeeLoan(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "employee_loans"

    employee_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    loan_amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    remaining_amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    monthly_deduction: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    start_date: Mapped[date] = mapped_column(Date, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)


class EmployeeAdvance(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "employee_advances"

    employee_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    remaining_amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    advance_date: Mapped[date] = mapped_column(Date, nullable=False)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)


class PayrollAccountingProfile(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payroll_accounting_profiles"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    department_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    salary_expense_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    employer_contribution_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    payable_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    deduction_account_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)


class PayrollHistory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payroll_history"

    payroll_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    action: Mapped[str] = mapped_column(String(50), nullable=False)
    actor_user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)


class PayrollSettlement(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "payroll_settlements"

    payroll_period_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    settlement_date: Mapped[date] = mapped_column(Date, nullable=False)
    total_amount: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    voucher_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
