"""factory_boy model factories for use in tests.

Usage
-----
::

    from tests.factories import TenantFactory, UserFactory, RoleFactory

    # Create objects in the DB (requires pytest ``db`` fixture or
    # ``@pytest.mark.django_db``):
    tenant = TenantFactory()
    user   = UserFactory()
    role   = RoleFactory(tenant=tenant)

    # Build (no DB hit):
    tenant = TenantFactory.build()
"""

from __future__ import annotations

import factory
from factory.django import DjangoModelFactory


# ---------------------------------------------------------------------------
# TenantFactory
# ---------------------------------------------------------------------------

class TenantFactory(DjangoModelFactory):
    """Minimal Tenant with a unique slug."""

    class Meta:
        model = "tenants.Tenant"

    slug = factory.Sequence(lambda n: f"tenant-{n}")
    name = factory.LazyAttribute(lambda o: o.slug.replace("-", " ").title())
    status = "active"


# ---------------------------------------------------------------------------
# UserFactory
# ---------------------------------------------------------------------------

class UserFactory(DjangoModelFactory):
    """User with a unique mobile number (E.164) and optional email."""

    class Meta:
        model = "accounts.User"

    # Mobile is the primary login key — must be globally unique.
    mobile = factory.Sequence(lambda n: f"+9890{n:08d}")
    # email must also be unique; derive it from the sequence counter.
    email = factory.LazyAttribute(lambda o: f"{o.mobile.lstrip('+')!s}@test.example")
    username = factory.Sequence(lambda n: f"user{n}")
    is_active = True
    password = factory.PostGenerationMethodCall("set_password", "testpass123")


class AdminUserFactory(UserFactory):
    """Superuser variant — use for staff-only endpoints."""

    is_staff = True
    is_superuser = True


# ---------------------------------------------------------------------------
# RoleFactory
# ---------------------------------------------------------------------------

class RoleFactory(DjangoModelFactory):
    """Tenant-scoped role with a unique (tenant, code) pair."""

    class Meta:
        model = "iam.Role"

    tenant = factory.SubFactory(TenantFactory)
    code = factory.Sequence(lambda n: f"role-{n}")
    name = factory.LazyAttribute(lambda o: o.code.replace("-", " ").title())
    is_system = False


# ===========================================================================
# HR Factories
# ===========================================================================

class JobTitleFactory(DjangoModelFactory):
    """Per-tenant job title."""

    class Meta:
        model = "hr.JobTitle"

    tenant = factory.SubFactory(TenantFactory)
    title = factory.Sequence(lambda n: f"Job Title {n}")
    level = 3


class EmployeeFactory(DjangoModelFactory):
    """Core employee — requires a pre-existing OrganizationNode passed as
    ``organization_node``.  Wrap in a fixture that provides it."""

    class Meta:
        model = "hr.Employee"
        # employee_no is auto-generated in the service layer; skip the DB
        # unique constraint by generating a unique value here.
        exclude = ["_seq"]

    _seq = factory.Sequence(lambda n: n)

    tenant = factory.SubFactory(TenantFactory)
    # Caller must override organization_node with a real node for the same tenant.
    organization_node = None  # type: ignore[assignment]
    first_name = factory.Faker("first_name")
    last_name = factory.Faker("last_name")
    display_name = factory.LazyAttribute(lambda o: f"{o.first_name} {o.last_name}")
    employee_no = factory.LazyAttribute(lambda o: f"EMP-{o._seq:04d}")
    hire_date = factory.Faker("date_object")
    employment_type = "full_time"
    status = "active"
    national_id = ""
    work_email = factory.LazyAttribute(
        lambda o: f"{o.first_name.lower()}.{o.last_name.lower()}{o._seq}@test.example"
    )


class LeaveTypeFactory(DjangoModelFactory):
    """Tenant leave type with upfront allocation."""

    class Meta:
        model = "hr.LeaveType"

    tenant = factory.SubFactory(TenantFactory)
    name = factory.Sequence(lambda n: f"Leave Type {n}")
    code = factory.Sequence(lambda n: f"LT{n}")
    is_paid = True
    requires_approval = True
    max_days_per_year = 21
    accrual_method = "upfront"


class LeaveRequestFactory(DjangoModelFactory):
    """Leave request in DRAFT status."""

    class Meta:
        model = "hr.LeaveRequest"

    tenant = factory.SubFactory(TenantFactory)
    employee = factory.SubFactory(EmployeeFactory)
    leave_type = factory.SubFactory(LeaveTypeFactory)
    from_date = factory.Faker("date_object")
    to_date = factory.LazyAttribute(lambda o: o.from_date)
    days_requested = 1
    status = "draft"
    reason = ""


class ShiftDefinitionFactory(DjangoModelFactory):
    """Standard 9-to-5 shift."""

    class Meta:
        model = "hr.ShiftDefinition"

    tenant = factory.SubFactory(TenantFactory)
    name = factory.Sequence(lambda n: f"Shift {n}")
    start_time = "09:00:00"
    end_time = "17:00:00"
    work_days = [0, 1, 2, 3, 4]  # Mon–Fri
    break_minutes = 30


class AttendanceRecordFactory(DjangoModelFactory):
    """Manual attendance record — check-in only by default."""

    class Meta:
        model = "hr.AttendanceRecord"

    employee = factory.SubFactory(EmployeeFactory)
    date = factory.Faker("date_object")
    check_in = factory.LazyAttribute(
        lambda _: __import__("datetime").time(9, 0)
    )
    check_out = None
    source = "manual"
    is_finalized = False
