"""HR domain events and notification subscribers.

Events follow the ``hr.<entity>.<action>`` convention.
Subscribers (registered via @subscribe) handle notifications and other
cross-cutting concerns for each event.
"""

from __future__ import annotations

import structlog

from simorgh.apps.events.bus import register_event, subscribe

_log = structlog.get_logger("simorgh.hr.events")


def register_all() -> None:
    """Register all HR domain events into the event bus registry."""

    register_event(
        "hr.employee.hired",
        description="A new employee record was created (hired).",
        payload_keys=("employee_id", "tenant_id", "department_id"),
    )
    register_event(
        "hr.employee.terminated",
        description="An employee was terminated.",
        payload_keys=("employee_id", "tenant_id", "termination_date"),
    )
    register_event(
        "hr.employee.resigned",
        description="An employee resigned.",
        payload_keys=("employee_id", "tenant_id", "resignation_date"),
    )
    register_event(
        "hr.employee.department_changed",
        description="An employee was moved to a different department.",
        payload_keys=("employee_id", "from_dept_id", "to_dept_id", "tenant_id"),
    )
    register_event(
        "hr.employee.manager_changed",
        description="An employee's manager was changed.",
        payload_keys=("employee_id", "from_manager_id", "to_manager_id", "tenant_id"),
    )
    register_event(
        "hr.employee.job_title_changed",
        description="An employee's job title was changed.",
        payload_keys=("employee_id", "from_title_id", "to_title_id", "tenant_id"),
    )

    # ── Leave (S4.2) ──────────────────────────────────────────────────────────
    register_event(
        "hr.leave.requested",
        description="A leave request was submitted.",
        payload_keys=("request_id", "employee_id", "leave_type_id", "tenant_id"),
    )
    register_event(
        "hr.leave.approved",
        description="A leave request was approved.",
        payload_keys=("request_id", "employee_id", "approved_by", "tenant_id"),
    )
    register_event(
        "hr.leave.rejected",
        description="A leave request was rejected.",
        payload_keys=("request_id", "employee_id", "rejected_by", "reason", "tenant_id"),
    )
    register_event(
        "hr.leave.cancelled",
        description="A leave request was cancelled.",
        payload_keys=("request_id", "employee_id", "tenant_id"),
    )

    # ── Attendance (S4.3) ─────────────────────────────────────────────────────
    register_event(
        "hr.attendance.checked_in",
        description="An employee checked in.",
        payload_keys=("employee_id", "date", "time", "tenant_id"),
    )
    register_event(
        "hr.attendance.finalized",
        description="An attendance record was finalized.",
        payload_keys=("employee_id", "date", "work_minutes", "tenant_id"),
    )

    # ── Position ───────────────────────────────────────────────────────────────
    register_event(
        "hr.position.created",
        description="A new position was created.",
        payload_keys=("position_id", "tenant_id", "department_id"),
    )
    register_event(
        "hr.position.filled",
        description="A position was filled by an employee.",
        payload_keys=("position_id", "employee_id", "tenant_id"),
    )
    register_event(
        "hr.position.vacated",
        description="An employee left a position.",
        payload_keys=("position_id", "employee_id", "tenant_id"),
    )

    # ── Employment ─────────────────────────────────────────────────────────────
    register_event(
        "hr.employment.created",
        description="An employment record was created.",
        payload_keys=("employment_id", "employee_id", "tenant_id"),
    )
    register_event(
        "hr.employment.terminated",
        description="An employment record was terminated.",
        payload_keys=("employment_id", "employee_id", "tenant_id", "termination_date"),
    )

    # ── Documents ─────────────────────────────────────────────────────────────
    register_event(
        "hr.document.expiring_soon",
        description="An employee document is expiring within the warning window.",
        payload_keys=("document_id", "employee_id", "tenant_id", "expiry_date", "doc_type"),
    )


# ---------------------------------------------------------------------------
# Notification subscribers
# ---------------------------------------------------------------------------

def _employee_org_node_id(employee_id: int) -> int | None:
    """Return the organization_node_id for an employee (used by notif dispatcher)."""
    try:
        from simorgh.apps.hr_core.models import Employee
        return Employee.objects.filter(pk=employee_id).values_list(
            "organization_node_id", flat=True
        ).first()
    except Exception:
        return None


@subscribe("hr.leave.requested")
def _notify_leave_requested(payload: dict) -> None:
    """Notify the employee's manager that a new leave request needs approval."""
    try:
        from simorgh.apps.hr_core.models import Employee, LeaveRequest
        from simorgh.apps.notifications import services as notif_svc

        request_id = payload["request_id"]
        tenant_id = payload["tenant_id"]

        req = LeaveRequest.objects.select_related(
            "employee__manager__user", "leave_type"
        ).get(pk=request_id)

        manager = req.employee.manager
        if manager is None or manager.user_id is None:
            return

        org_node_id = _employee_org_node_id(req.employee_id)
        if org_node_id is None:
            return

        notif_svc.dispatch(
            "hr.leave.requested",
            recipients=[manager.user_id],
            context={
                "employee_name": req.employee.display_name or str(req.employee),
                "leave_type": req.leave_type.name,
                "from_date": req.from_date.isoformat(),
                "to_date": req.to_date.isoformat(),
                "days": str(req.days_requested),
            },
            tenant_id=tenant_id,
            organization_node_id=org_node_id,
        )
    except Exception:
        _log.exception("hr.notify_leave_requested.error", payload=payload)


@subscribe("hr.leave.approved")
def _notify_leave_approved(payload: dict) -> None:
    """Notify the employee that their leave was approved."""
    try:
        from simorgh.apps.hr_core.models import LeaveRequest
        from simorgh.apps.notifications import services as notif_svc

        request_id = payload["request_id"]
        tenant_id = payload["tenant_id"]

        req = LeaveRequest.objects.select_related(
            "employee__user", "leave_type"
        ).get(pk=request_id)

        if req.employee.user_id is None:
            return

        org_node_id = _employee_org_node_id(req.employee_id)
        if org_node_id is None:
            return

        notif_svc.dispatch(
            "hr.leave.approved",
            recipients=[req.employee.user_id],
            context={
                "leave_type": req.leave_type.name,
                "from_date": req.from_date.isoformat(),
                "to_date": req.to_date.isoformat(),
                "days": str(req.days_requested),
            },
            tenant_id=tenant_id,
            organization_node_id=org_node_id,
        )
    except Exception:
        _log.exception("hr.notify_leave_approved.error", payload=payload)


@subscribe("hr.leave.rejected")
def _notify_leave_rejected(payload: dict) -> None:
    """Notify the employee that their leave was rejected."""
    try:
        from simorgh.apps.hr_core.models import LeaveRequest
        from simorgh.apps.notifications import services as notif_svc

        request_id = payload["request_id"]
        tenant_id = payload["tenant_id"]

        req = LeaveRequest.objects.select_related(
            "employee__user", "leave_type"
        ).get(pk=request_id)

        if req.employee.user_id is None:
            return

        org_node_id = _employee_org_node_id(req.employee_id)
        if org_node_id is None:
            return

        notif_svc.dispatch(
            "hr.leave.rejected",
            recipients=[req.employee.user_id],
            context={
                "leave_type": req.leave_type.name,
                "from_date": req.from_date.isoformat(),
                "to_date": req.to_date.isoformat(),
                "reason": payload.get("reason", ""),
            },
            tenant_id=tenant_id,
            organization_node_id=org_node_id,
        )
    except Exception:
        _log.exception("hr.notify_leave_rejected.error", payload=payload)


@subscribe("hr.leave.cancelled")
def _notify_leave_cancelled(payload: dict) -> None:
    """Notify the manager that a leave request was cancelled by the employee."""
    try:
        from simorgh.apps.hr_core.models import LeaveRequest
        from simorgh.apps.notifications import services as notif_svc

        request_id = payload["request_id"]
        tenant_id = payload["tenant_id"]

        req = LeaveRequest.objects.select_related(
            "employee__manager__user", "leave_type"
        ).get(pk=request_id)

        manager = req.employee.manager
        if manager is None or manager.user_id is None:
            return

        org_node_id = _employee_org_node_id(req.employee_id)
        if org_node_id is None:
            return

        notif_svc.dispatch(
            "hr.leave.cancelled",
            recipients=[manager.user_id],
            context={
                "employee_name": req.employee.display_name or str(req.employee),
                "leave_type": req.leave_type.name,
                "from_date": req.from_date.isoformat(),
                "to_date": req.to_date.isoformat(),
            },
            tenant_id=tenant_id,
            organization_node_id=org_node_id,
        )
    except Exception:
        _log.exception("hr.notify_leave_cancelled.error", payload=payload)


@subscribe("hr.document.expiring_soon")
def _notify_document_expiring(payload: dict) -> None:
    """Notify the employee and HR admin about an expiring document."""
    try:
        from simorgh.apps.hr_core.models import Employee, EmployeeDocument
        from simorgh.apps.notifications import services as notif_svc

        doc_id = payload["document_id"]
        tenant_id = payload["tenant_id"]

        doc = EmployeeDocument.objects.select_related("employee__user").get(pk=doc_id)
        employee = doc.employee

        if employee.user_id is None:
            return

        org_node_id = _employee_org_node_id(employee.pk)
        if org_node_id is None:
            return

        notif_svc.dispatch(
            "hr.document.expiring_soon",
            recipients=[employee.user_id],
            context={
                "doc_type": payload.get("doc_type", ""),
                "expiry_date": payload.get("expiry_date", ""),
                "employee_name": employee.display_name or str(employee),
            },
            tenant_id=tenant_id,
            organization_node_id=org_node_id,
        )
    except Exception:
        _log.exception("hr.notify_document_expiring.error", payload=payload)
