"""Queue with chat notification integration.

This module provides a JobQueue wrapper that sends chat notifications
for job lifecycle events (complete, fail, retry).

Example:
    >>> from oclawma.notifications.queue_integration import NotifyingJobQueue
    >>> from oclawma.notifications.chat import ChatNotificationConfig
    >>> config = ChatNotificationConfig(
    ...     discord_webhook="https://discord.com/api/webhooks/...",
    ...     triggers={"on_fail": True, "on_complete": True}
    ... )
    >>> queue = NotifyingJobQueue("/path/to/queue.db", notification_config=config)
    >>> job = queue.enqueue({"task": "process_data"})
    >>> # Notifications sent automatically on job events
"""

from __future__ import annotations

import logging
from pathlib import Path
from typing import Any, Callable

from oclawma.encryption import EncryptionManager
from oclawma.notifications.chat import ChatNotificationConfig, ChatNotifier
from oclawma.queue.dlq import DeadLetterQueue
from oclawma.queue.models import Job
from oclawma.queue.queue import (
    DEFAULT_BASE_DELAY_SECONDS,
    DEFAULT_MAX_DELAY_SECONDS,
    DEFAULT_MAX_RETRIES,
    DLQCallback,
    JobQueue,
)
from oclawma.ratelimit import RateLimitConfig, RateLimiter

logger = logging.getLogger(__name__)


class NotifyingJobQueue(JobQueue):
    """JobQueue with integrated chat notifications.

    This class extends JobQueue to automatically send chat notifications
    when jobs complete, fail, or are retried. Notifications are configurable
    via ChatNotificationConfig.

    Features:
    - Automatic notifications on job complete/fail/retry
    - Discord and Slack webhook support
    - Rate limiting to prevent spam
    - @mention support for critical jobs
    - Per-job-type routing

    Example:
        >>> config = ChatNotificationConfig(
        ...     discord_webhook="https://discord.com/api/webhooks/...",
        ...     triggers={"on_fail": True, "on_complete": True}
        ... )
        >>> queue = NotifyingJobQueue("/path/to/queue.db", notification_config=config)
        >>> job = queue.enqueue({"task": "send_email"})
        >>> # Complete the job - notification sent automatically
        >>> queue.complete(job.id)
    """

    def __init__(
        self,
        db_path: str | Path = ":memory:",
        max_retries: int = DEFAULT_MAX_RETRIES,
        base_delay_seconds: float = DEFAULT_BASE_DELAY_SECONDS,
        max_delay_seconds: float = DEFAULT_MAX_DELAY_SECONDS,
        dlq: DeadLetterQueue | None = None,
        on_dlq: DLQCallback | None = None,
        rate_limiter: RateLimiter | RateLimitConfig | None = None,
        rate_limit_behavior: str = "block",
        encryption_manager: EncryptionManager | None = None,
        notification_config: ChatNotificationConfig | None = None,
    ) -> None:
        """Initialize the notifying job queue.

        Args:
            db_path: Path to SQLite database
            max_retries: Default max retries for new jobs
            base_delay_seconds: Base delay for exponential backoff
            max_delay_seconds: Maximum delay between retries
            dlq: Optional DeadLetterQueue for exhausted jobs
            on_dlq: Optional callback when job moves to DLQ
            rate_limiter: Optional RateLimiter instance or config
            rate_limit_behavior: Behavior when rate limited
            encryption_manager: Optional EncryptionManager for payloads
            notification_config: Optional notification configuration
        """
        super().__init__(
            db_path=db_path,
            max_retries=max_retries,
            base_delay_seconds=base_delay_seconds,
            max_delay_seconds=max_delay_seconds,
            dlq=dlq,
            on_dlq=on_dlq,
            rate_limiter=rate_limiter,
            rate_limit_behavior=rate_limit_behavior,
            encryption_manager=encryption_manager,
        )

        self.notification_config = notification_config
        self._notifier: ChatNotifier | None = None

        if notification_config and notification_config.enabled:
            self._notifier = ChatNotifier(notification_config)
            logger.info("Chat notifications enabled")

    def _notify_if_enabled(self, job: Job, trigger: str) -> None:
        """Send notification if enabled for trigger.

        Args:
            job: Job to notify about
            trigger: Trigger type (on_complete, on_fail, on_retry)
        """
        if self._notifier is None or not self.notification_config:
            return

        try:
            from oclawma.notifications.chat import NotificationTrigger

            notification_trigger = NotificationTrigger(trigger)
            if not self.notification_config.should_trigger(notification_trigger):
                return

            if trigger == "on_complete":
                self._notifier.notify_job_completed(job)
            elif trigger == "on_fail":
                self._notifier.notify_job_failed(job)
            elif trigger == "on_retry":
                self._notifier.notify_job_retry(job)

        except Exception as e:
            # Never let notifications break the queue
            logger.warning(f"Failed to send notification: {e}")

    def complete(self, job_id: int) -> Job:
        """Mark a job as completed and notify.

        Args:
            job_id: Job identifier

        Returns:
            The completed job
        """
        job = super().complete(job_id)
        self._notify_if_enabled(job, "on_complete")
        return job

    def fail(self, job_id: int, error: str | None = None) -> Job:
        """Mark a job as failed and notify.

        Args:
            job_id: Job identifier
            error: Error message

        Returns:
            The failed job
        """
        job = super().fail(job_id, error)
        self._notify_if_enabled(job, "on_fail")
        return job

    def retry(self, job_id: int, error: Exception | None = None) -> Job | None:
        """Retry a failed job and notify.

        Args:
            job_id: Job identifier
            error: Optional exception that caused the failure

        Returns:
            Updated job if retry scheduled, None if max retries exceeded
        """
        job = self.store.get(job_id)

        # Check if we're actually going to retry (before calling super)
        will_retry = job.is_retryable() if job.status.value == "failed" else False

        result = super().retry(job_id, error)

        # Only notify if we're actually retrying (not moving to DLQ)
        if result and will_retry:
            self._notify_if_enabled(result, "on_retry")

        return result

    def process_next(self, handler: Callable[[Job], None]) -> bool:
        """Process the next job with notification support.

        Args:
            handler: Function to process the job

        Returns:
            True if a job was processed
        """
        job = self.dequeue()
        if not job:
            return False

        try:
            handler(job)
            self.complete(job.id)
            return True
        except Exception as e:
            error_msg = str(e)
            self.fail(job.id, error_msg)
            self.retry(job.id, error=e)
            raise

    def close(self) -> None:
        """Close the queue and release resources."""
        if self._notifier:
            self._notifier.close()
            self._notifier = None
        super().close()

    def test_notifications(self) -> list[Any]:
        """Test notification webhooks.

        Returns:
            List of test results
        """
        if self._notifier is None:
            return []
        return self._notifier.test_all_webhooks()

    def update_notification_config(self, config: ChatNotificationConfig) -> None:
        """Update notification configuration.

        Args:
            config: New notification configuration
        """
        # Close existing notifier
        if self._notifier:
            self._notifier.close()

        self.notification_config = config

        if config.enabled:
            self._notifier = ChatNotifier(config)
            logger.info("Chat notifications updated and enabled")
        else:
            self._notifier = None
            logger.info("Chat notifications disabled")


class NotificationMixin:
    """Mixin class to add notifications to any JobQueue subclass.

    This mixin can be used with custom JobQueue subclasses to add
    notification support without inheriting from NotifyingJobQueue.

    Example:
        >>> class MyCustomQueue(JobQueue):
        ...     pass
        >>> class MyNotifyingQueue(NotificationMixin, MyCustomQueue):
        ...     def __init__(self, *args, notification_config=None, **kwargs):
        ...         super().__init__(*args, **kwargs)
        ...         self._init_notifications(notification_config)
    """

    def _init_notifications(self, config: ChatNotificationConfig | None) -> None:
        """Initialize notification support.

        Args:
            config: Notification configuration
        """
        self.notification_config = config
        self._notifier: ChatNotifier | None = None

        if config and config.enabled:
            self._notifier = ChatNotifier(config)
            logger.info("Chat notifications enabled via mixin")

    def _notify_if_enabled(self, job: Job, trigger: str) -> None:
        """Send notification if enabled.

        Args:
            job: Job to notify about
            trigger: Trigger type
        """
        if self._notifier is None:
            return

        try:
            from oclawma.notifications.chat import NotificationTrigger

            notification_trigger = NotificationTrigger(trigger)
            if not self.notification_config.should_trigger(notification_trigger):
                return

            if trigger == "on_complete":
                self._notifier.notify_job_completed(job)
            elif trigger == "on_fail":
                self._notifier.notify_job_failed(job)
            elif trigger == "on_retry":
                self._notifier.notify_job_retry(job)
        except Exception as e:
            logger.warning(f"Failed to send notification: {e}")

    def complete(self, job_id: int) -> Job:
        """Complete job with notification."""
        # Note: This assumes the parent class has a complete method
        # that we can call via super(). In actual use, ensure MRO is correct.
        job = super().complete(job_id)  # type: ignore[misc]
        self._notify_if_enabled(job, "on_complete")
        return job

    def fail(self, job_id: int, error: str | None = None) -> Job:
        """Fail job with notification."""
        job = super().fail(job_id, error)  # type: ignore[misc]
        self._notify_if_enabled(job, "on_fail")
        return job

    def close(self) -> None:
        """Close with cleanup."""
        if self._notifier:
            self._notifier.close()
            self._notifier = None
        super().close()  # type: ignore[misc]
