"""
Tests for the AsyncTaskQueue class.
"""

import tempfile
import time
from datetime import datetime
from pathlib import Path
from threading import Event
from unittest.mock import MagicMock

import pytest

from backend.web.workers.async_queue import AsyncTask, AsyncTaskQueue


class TestAsyncTask:
    """Tests for AsyncTask dataclass."""

    def test_create_task(self):
        """Test creating an AsyncTask."""
        task = AsyncTask(
            request_id="test-id",
            api_key="test-key",
            file_path=Path("/tmp/test.pdf"),
            filename="test.pdf",
        )

        assert task.request_id == "test-id"
        assert task.api_key == "test-key"
        assert task.filename == "test.pdf"
        assert task.priority == 0
        assert task.created_at is not None


class TestAsyncTaskQueue:
    """Tests for AsyncTaskQueue."""

    def test_init(self):
        """Test queue initialization."""
        queue = AsyncTaskQueue(max_size=50, worker_count=2)

        assert queue._worker_count == 2
        assert queue._queue.maxsize == 50
        assert not queue._started

    def test_submit_task(self, task_queue, sample_task):
        """Test submitting a task to the queue."""
        success = task_queue.submit(sample_task)

        assert success is True
        assert task_queue.get_queue_depth() == 1

    def test_submit_when_full(self, sample_task):
        """Test submitting to a full queue."""
        queue = AsyncTaskQueue(max_size=1, worker_count=1)

        # Submit first task
        queue.submit(sample_task)

        # Create second task
        task2 = AsyncTask(
            request_id="test-2",
            api_key="test-key",
            file_path=sample_task.file_path,
            filename="test2.pdf",
        )

        # Queue should be full
        success = queue.submit(task2)
        assert success is False

    def test_get_queue_depth(self, task_queue, sample_task):
        """Test getting queue depth."""
        assert task_queue.get_queue_depth() == 0

        task_queue.submit(sample_task)
        assert task_queue.get_queue_depth() == 1

    def test_start_and_stop(self, task_queue):
        """Test starting and stopping the queue."""
        handler = MagicMock()

        task_queue.start(handler)
        assert task_queue._started is True
        assert task_queue.is_running is True
        assert len(task_queue._workers) == 1

        task_queue.stop(timeout=5.0)
        assert task_queue._started is False
        assert task_queue.is_running is False
        assert len(task_queue._workers) == 0

    def test_worker_processes_task(self, sample_task):
        """Test that worker thread processes tasks."""
        queue = AsyncTaskQueue(max_size=10, worker_count=1)
        processed = Event()

        def handler(task):
            processed.set()

        queue.start(handler)
        queue.submit(sample_task)

        # Wait for processing
        assert processed.wait(timeout=5.0)

        queue.stop()

    def test_worker_handles_errors(self, sample_task):
        """Test that worker handles errors gracefully."""
        queue = AsyncTaskQueue(max_size=10, worker_count=1)
        error_handled = Event()

        def failing_handler(task):
            error_handled.set()
            raise ValueError("Test error")

        queue.start(failing_handler)
        queue.submit(sample_task)

        # Should not crash
        assert error_handled.wait(timeout=5.0)
        time.sleep(0.5)  # Give time for error handling

        assert queue.is_running

        queue.stop()

    def test_processing_tracking(self, task_queue, sample_task):
        """Test tracking of processing tasks."""
        processed = Event()

        def slow_handler(task):
            processed.set()
            time.sleep(0.5)

        task_queue.start(slow_handler)
        task_queue.submit(sample_task)

        # Wait for processing to start
        assert processed.wait(timeout=5.0)

        # Task should be in processing set
        assert task_queue.get_processing_count() == 1
        assert task_queue.is_processing(sample_task.request_id)

        # Wait for completion
        time.sleep(1.0)

        assert task_queue.get_processing_count() == 0
        assert not task_queue.is_processing(sample_task.request_id)

        task_queue.stop()

    def test_multiple_workers(self, sample_task):
        """Test queue with multiple workers."""
        queue = AsyncTaskQueue(max_size=10, worker_count=3)
        processed_count = []

        def handler(task):
            processed_count.append(task.request_id)
            time.sleep(0.1)

        queue.start(handler)

        # Submit multiple tasks
        for i in range(5):
            task = AsyncTask(
                request_id=f"task-{i}",
                api_key="test-key",
                file_path=sample_task.file_path,
                filename=f"test-{i}.pdf",
            )
            queue.submit(task)

        # Wait for all tasks
        time.sleep(2.0)

        assert len(processed_count) == 5

        queue.stop()

    def test_graceful_shutdown(self, sample_task):
        """Test graceful shutdown waits for current task."""
        queue = AsyncTaskQueue(max_size=10, worker_count=1)
        started = Event()
        finished = Event()

        def slow_handler(task):
            started.set()
            time.sleep(0.5)
            finished.set()

        queue.start(slow_handler)
        queue.submit(sample_task)

        # Wait for processing to start
        assert started.wait(timeout=5.0)

        # Stop should wait for task to finish
        queue.stop(timeout=5.0)

        assert finished.is_set()

    def test_double_start(self, task_queue):
        """Test that starting twice doesn't create duplicate workers."""
        handler = MagicMock()

        task_queue.start(handler)
        assert len(task_queue._workers) == 1

        # Starting again should not add more workers
        task_queue.start(handler)
        assert len(task_queue._workers) == 1

        task_queue.stop()
