"""Seed HR module with default data.

Idempotent — safe to re-run at any time. All writes use ``update_or_create``
or ``get_or_create`` so manual adjustments in admin are preserved.

Usage
-----
    # Seed for every active tenant
    python manage.py seed_hr

    # Seed only a specific tenant (by slug or pk)
    python manage.py seed_hr --tenant acme
    python manage.py seed_hr --tenant 3

Data seeded per tenant
----------------------
- 6 Default Job Titles
- 6 Default Leave Types
- Employee profiles derived from organizational users via memberships
- Org-chart relationships read from OrganizationNode hierarchy

Important
---------
Employees are created ONLY for users who already have an active Membership in
the tenant.  No standalone employee records — every employee must correspond to
a real organizational user.
"""

from __future__ import annotations

import logging
from typing import Any

from django.core.management.base import BaseCommand, CommandError
from django.db import transaction

from simorgh.apps.accounts.selectors import list_users_for_tenant
from simorgh.apps.hr_core import services as hr_services
from simorgh.apps.hr_core.models import Employee, JobTitle, LeaveType
from simorgh.apps.memberships.models import Membership, MembershipStatus
from simorgh.apps.organizations.models import OrganizationNode

_log = logging.getLogger("simorgh.hr")

# ---------------------------------------------------------------------------
# Seed data definitions
# ---------------------------------------------------------------------------

DEFAULT_JOB_TITLES: list[dict[str, Any]] = [
    {"title": "مدیرعامل",      "level": 10},
    {"title": "معاون",          "level": 9},
    {"title": "مدیر بخش",      "level": 8},
    {"title": "سرپرست",        "level": 6},
    {"title": "کارشناس ارشد", "level": 5},
    {"title": "کارشناس",       "level": 4},
]

DEFAULT_LEAVE_TYPES: list[dict[str, Any]] = [
    {
        "name": "مرخصی استحقاقی",
        "code": "annual",
        "color": "#4A90D9",
        "is_paid": True,
        "requires_approval": True,
        "max_days_per_year": 30,
        "carry_forward_days": 5,
        "accrual_method": "upfront",
    },
    {
        "name": "مرخصی استعلاجی",
        "code": "sick",
        "color": "#E74C3C",
        "is_paid": True,
        "requires_approval": True,
        "max_days_per_year": 15,
        "carry_forward_days": 0,
        "accrual_method": "upfront",
    },
    {
        "name": "مرخصی بدون حقوق",
        "code": "unpaid",
        "color": "#95A5A6",
        "is_paid": False,
        "requires_approval": True,
        "max_days_per_year": 30,
        "carry_forward_days": 0,
        "accrual_method": "none",
    },
    {
        "name": "مرخصی اضطراری",
        "code": "emergency",
        "color": "#F39C12",
        "is_paid": True,
        "requires_approval": False,
        "max_days_per_year": 5,
        "carry_forward_days": 0,
        "accrual_method": "upfront",
    },
    {
        "name": "مرخصی زایمان",
        "code": "maternity",
        "color": "#9B59B6",
        "is_paid": True,
        "requires_approval": True,
        "max_days_per_year": 90,
        "carry_forward_days": 0,
        "accrual_method": "upfront",
    },
    {
        "name": "مرخصی ساعتی",
        "code": "hourly",
        "color": "#1ABC9C",
        "is_paid": True,
        "requires_approval": True,
        "max_days_per_year": 5,
        "carry_forward_days": 0,
        "accrual_method": "monthly",
    },
]

# ---------------------------------------------------------------------------
# Public helper — callable from signals / tests without a Command instance
# ---------------------------------------------------------------------------


@transaction.atomic
def seed_tenant_defaults(tenant) -> dict[str, int]:
    """Idempotent seed of HR defaults for a single tenant.

    Called automatically via ``post_save`` signal on ``Tenant`` creation (if
    wired) and can be invoked directly in tests or migration scripts.

    Returns a dict of created-counts per entity type.
    """
    from datetime import date

    counts: dict[str, int] = {"job_titles": 0, "leave_types": 0, "employees": 0}

    # ── Resolve root organization node ───────────────────────────────────
    root_node = (
        OrganizationNode.objects.filter(tenant=tenant, parent__isnull=True)
        .order_by("depth", "pk")
        .first()
    )
    if root_node is None:
        _log.warning(
            "seed_tenant_defaults: no root org node for tenant %s — skipping", tenant.pk
        )
        return counts

    # ── 1. Seed Job Titles ───────────────────────────────────────────────
    for jt_def in DEFAULT_JOB_TITLES:
        _, created = JobTitle.objects.get_or_create(
            tenant=tenant,
            title=jt_def["title"],
            defaults={
                "level": jt_def["level"],
                "organization_node": root_node,
            },
        )
        if created:
            counts["job_titles"] += 1

    # ── 2. Seed Leave Types ──────────────────────────────────────────────
    for lt_def in DEFAULT_LEAVE_TYPES:
        _, created = LeaveType.objects.get_or_create(
            tenant=tenant,
            code=lt_def["code"],
            defaults=lt_def,
        )
        if created:
            counts["leave_types"] += 1

    # ── 3. Build org-node index ──────────────────────────────────────────
    # ── 4. Build user → org-node map from memberships ────────────────────
    org_users = list_users_for_tenant(tenant.pk)
    if not org_users:
        _log.info("seed_tenant_defaults: no org users for tenant %s", tenant.pk)
        return counts

    user_org_node_map: dict[int, OrganizationNode] = {}
    memberships = (
        Membership.objects.filter(
            tenant=tenant,
            status=MembershipStatus.ACTIVE,
        )
        .select_related("organization_node")
        .prefetch_related("users")
    )
    for m in memberships:
        for u in m.users.all():
            current = user_org_node_map.get(u.pk)
            if current is None or m.organization_node.depth > current.depth:
                user_org_node_map[u.pk] = m.organization_node

    # ── 5. Resolve job title list ────────────────────────────────────────
    job_titles = list(JobTitle.objects.filter(tenant=tenant).order_by("level"))
    if not job_titles:
        return counts

    # ── 6. Create Employee records from organizational users ─────────────
    for user in org_users:
        if Employee.objects.filter(tenant=tenant, user=user).exists():
            continue

        emp_org_node = user_org_node_map.get(user.pk, root_node)
        node_depth = emp_org_node.depth

        title_index = min(node_depth, len(job_titles) - 1)
        job_title = job_titles[title_index]

        # Manager = first active employee at the parent org node.
        manager_id = None
        if emp_org_node.parent_id:
            manager = Employee.objects.filter(
                tenant=tenant,
                department=emp_org_node.parent,
                status__in=["active", "on_leave"],
            ).first()
            if manager:
                manager_id = manager.pk

        try:
            hr_services.create_employee(
                tenant_id=tenant.pk,
                organization_node_id=emp_org_node.pk,
                first_name=user.first_name or "کاربر",
                last_name=user.last_name or "",
                hire_date=getattr(user, "date_joined", date.today()),
                user_id=user.pk,
                mobile=getattr(user, "mobile", ""),
                work_email=getattr(user, "email", ""),
                department_id=emp_org_node.pk,
                manager_id=manager_id,
                job_title_id=job_title.pk,
            )
            counts["employees"] += 1
        except Exception as exc:
            _log.warning(
                "seed_tenant_defaults: failed creating employee for user %s: %s",
                user.pk,
                exc,
            )
            continue

    # ── 7. Backfill managers for employees whose manager was created later ──
    if counts["employees"] > 0:
        for employee in Employee.objects.filter(
            tenant=tenant,
            manager__isnull=True,
            department__parent__isnull=False,
        ).select_related("department__parent"):
            manager = (
                Employee.objects.filter(
                    tenant=tenant,
                    department=employee.department.parent,
                    status__in=["active", "on_leave"],
                )
                .exclude(pk=employee.pk)
                .first()
            )
            if manager:
                try:
                    hr_services.update_employee(employee, manager_id=manager.pk)
                except Exception:
                    continue

    return counts


# ---------------------------------------------------------------------------
# Command
# ---------------------------------------------------------------------------


class Command(BaseCommand):
    help = "Seed HR module with default job titles, leave types, and employee profiles from organizational users."

    def add_arguments(self, parser):
        parser.add_argument(
            "--tenant",
            default=None,
            help="Slug or PK of a specific tenant to seed (default: all active tenants).",
        )

    def handle(self, *args, **options):
        from simorgh.apps.tenants.models import Tenant, TenantStatus

        tenant_filter = options.get("tenant")

        if tenant_filter:
            qs = Tenant.objects.filter(status=TenantStatus.ACTIVE)
            try:
                pk = int(tenant_filter)
                qs = qs.filter(pk=pk)
            except ValueError:
                qs = qs.filter(slug=tenant_filter)
            if not qs.exists():
                raise CommandError(f"Tenant {tenant_filter!r} not found or not active.")
            tenants = list(qs)
        else:
            tenants = list(Tenant.objects.filter(status=TenantStatus.ACTIVE))

        if not tenants:
            self.stderr.write(
                self.style.WARNING("No active tenants found — nothing seeded.")
            )
            return

        self.stdout.write(
            self.style.MIGRATE_HEADING(
                f"Seeding HR for {len(tenants)} tenant(s)…"
            )
        )

        total = {"job_titles": 0, "leave_types": 0, "employees": 0}
        for tenant in tenants:
            created = self._seed_tenant(tenant)
            for k, v in created.items():
                total[k] += v

        self.stdout.write(
            self.style.SUCCESS(
                f"\nHR seeding complete: "
                f"job_titles={total['job_titles']}  "
                f"leave_types={total['leave_types']}  "
                f"employees={total['employees']}"
            )
        )

    # ── Per-tenant seed ──────────────────────────────────────────────────

    def _seed_tenant(self, tenant) -> dict[str, int]:
        """Run seed for a single tenant and return created-counts."""
        root_node = (
            OrganizationNode.objects.filter(tenant=tenant, parent__isnull=True)
            .order_by("depth", "pk")
            .first()
        )
        if root_node is None:
            self.stderr.write(
                self.style.WARNING(
                    f"  [{tenant.slug}] No root org node — skipping."
                )
            )
            return {"job_titles": 0, "leave_types": 0, "employees": 0}

        created = seed_tenant_defaults(tenant)

        self.stdout.write(
            f"  [{tenant.slug}] job_titles={created['job_titles']}  "
            f"leave_types={created['leave_types']}  "
            f"employees={created['employees']}"
        )
        return created
