"""Celery tasks for the helpdesk module.

All tasks are idempotent and safe to retry. They call service-layer methods
rather than implementing business logic themselves.
"""

from __future__ import annotations

import structlog
from celery import shared_task
from django.utils import timezone

from simorgh.apps.app_settings.registry import SettingsError

_log = structlog.get_logger("simorgh.helpdesk.tasks")


@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def check_sla_breaches(self) -> int:
    """Scan all overdue SLA timers and emit ``helpdesk.sla.breached`` events.

    Intended to run every minute via Celery Beat.
    Returns the number of newly breached timers.
    """
    from simorgh.apps.events.bus import dispatch
    from simorgh.apps.helpdesk.models import SLATimer

    now = timezone.now()
    overdue = SLATimer.objects.filter(
        due_at__lte=now,
        is_paused=False,
        is_resolved=False,
        breached_at__isnull=True,
    ).select_related("ticket")

    count = 0
    for timer in overdue:
        timer.breached_at = now
        timer.save(update_fields=["breached_at"])
        dispatch("helpdesk.sla.breached", {
            "ticket_id": timer.ticket_id,
            "tenant_id": timer.ticket.tenant_id,
            "breach_type": timer.breach_type,
            "due_at": timer.due_at.isoformat(),
        })
        count += 1

    if count:
        _log.info("helpdesk.tasks.sla_breaches_detected", count=count)
    return count


@shared_task(bind=True, max_retries=3, default_retry_delay=300)
def auto_close_solved_tickets(self) -> int:
    """Close tickets that have been in *solved* status past the configured delay.

    Runs hourly via Celery Beat. Auto-close delay is tenant-configurable via
    ``helpdesk.auto_close_hours`` setting.
    """
    from simorgh.apps.app_settings.services import resolve
    from simorgh.apps.helpdesk.models import Ticket, TicketStatus
    from simorgh.apps.helpdesk.services import TicketService

    default_hours = 168  # 7 days fallback if resolve fails.
    count = 0

    # Group by tenant to respect per-tenant configuration.
    tenant_ids = (
        Ticket.objects.filter(status=TicketStatus.SOLVED)
        .values_list("tenant_id", flat=True)
        .distinct()
    )

    for tenant_id in tenant_ids:
        try:
            hours = int(resolve("helpdesk.auto_close_hours", tenant_id=tenant_id))
        except (SettingsError, ValueError, TypeError):
            hours = default_hours

        cutoff = timezone.now() - __import__("datetime").timedelta(hours=hours)
        tickets = Ticket.objects.filter(
            tenant_id=tenant_id,
            status=TicketStatus.SOLVED,
            resolved_at__lte=cutoff,
        )
        for ticket in tickets:
            try:
                TicketService.transition(ticket, new_status=TicketStatus.CLOSED)
                count += 1
            except Exception as exc:
                _log.warning(
                    "helpdesk.tasks.auto_close_failed",
                    ticket_id=ticket.pk,
                    error=str(exc),
                )

    if count:
        _log.info("helpdesk.tasks.auto_closed", count=count)
    return count


@shared_task(bind=True, max_retries=3, default_retry_delay=600)
def send_csat_surveys(self) -> int:
    """Send CSAT survey notifications for recently-solved tickets.

    Evaluates ``helpdesk.csat_delay_hours`` per tenant.  Tickets solved
    past the delay threshold and not yet surveyed get a CSAT notification.

    Runs every 30 minutes via Celery Beat.
    """
    from simorgh.apps.app_settings.services import resolve
    from simorgh.apps.events.bus import dispatch
    from simorgh.apps.helpdesk.models import Ticket, TicketStatus

    default_delay = 24  # hours
    count = 0

    tenant_ids = (
        Ticket.objects.filter(status=TicketStatus.SOLVED, satisfaction_rating__isnull=True)
        .values_list("tenant_id", flat=True)
        .distinct()
    )

    for tenant_id in tenant_ids:
        try:
            delay_hours = int(resolve("helpdesk.csat_delay_hours", tenant_id=tenant_id))
        except (ValueError, TypeError):
            delay_hours = default_delay

        cutoff = timezone.now() - __import__("datetime").timedelta(hours=delay_hours)
        tickets = Ticket.objects.filter(
            tenant_id=tenant_id,
            status=TicketStatus.SOLVED,
            resolved_at__lte=cutoff,
            satisfaction_rating__isnull=True,
        )

        for ticket in tickets:
            dispatch("helpdesk.csat.survey_due", {
                "ticket_id": ticket.pk,
                "tenant_id": tenant_id,
                "queue_id": ticket.queue_id,
                "requester_id": ticket.requester_id,
                "resolved_at": ticket.resolved_at.isoformat() if ticket.resolved_at else None,
            })
            count += 1

    if count:
        _log.info("helpdesk.tasks.csat_surveys_dispatched", count=count)
    return count
