# tests/tasks/test_workflow_tasks.py
"""
Tests for workflow state management tasks.

These tests verify:
- Task signatures are correctly defined
- Tasks are bound (have access to self)
- Tasks return expected structure
- Tasks follow ADR-007 (transitions) and ADR-010 (PostgreSQL durability)

Note: These tests mock actual execution since they would require
database access and state machine operations in production.
"""

import uuid
from unittest.mock import patch


class TestRecoverStaleWorkflowsTask:
    """Tests for the recover_stale_workflows task."""

    def test_recover_stale_workflows_task_exists(self):
        """Test that recover_stale_workflows task is registered."""
        import app.tasks.workflow  # noqa: F401
        from app.celery_app import celery_app

        assert "app.tasks.workflow.recover_stale_workflows" in celery_app.tasks

    def test_recover_stale_workflows_is_bound_task(self):
        """Test that recover_stale_workflows is a bound task."""
        from app.tasks.workflow import recover_stale_workflows

        assert recover_stale_workflows.__bound__ is True

    def test_recover_stale_workflows_has_correct_name(self):
        """Test that recover_stale_workflows has the correct task name."""
        from app.tasks.workflow import recover_stale_workflows

        assert (
            recover_stale_workflows.name == "app.tasks.workflow.recover_stale_workflows"
        )

    def test_recover_stale_workflows_returns_expected_structure(self):
        """Test that recover_stale_workflows returns expected result."""
        from app.tasks.workflow import recover_stale_workflows

        result = recover_stale_workflows()

        assert isinstance(result, dict)
        assert "status" in result
        assert "recovered" in result
        assert result["status"] == "pending"
        assert result["recovered"] == 0


class TestExecuteWorkflowStepTask:
    """Tests for the execute_workflow_step task."""

    def test_execute_workflow_step_task_exists(self):
        """Test that execute_workflow_step task is registered."""
        import app.tasks.workflow  # noqa: F401
        from app.celery_app import celery_app

        assert "app.tasks.workflow.execute_workflow_step" in celery_app.tasks

    def test_execute_workflow_step_is_bound_task(self):
        """Test that execute_workflow_step is a bound task."""
        from app.tasks.workflow import execute_workflow_step

        assert execute_workflow_step.__bound__ is True

    def test_execute_workflow_step_returns_expected_structure(self):
        """Test that execute_workflow_step returns expected result."""
        from app.tasks.workflow import execute_workflow_step

        workflow_id = str(uuid.uuid4())
        transition = "start_planning"

        result = execute_workflow_step(workflow_id, transition)

        assert isinstance(result, dict)
        assert "status" in result
        assert "workflow_id" in result
        assert "transition" in result
        assert result["workflow_id"] == workflow_id
        assert result["transition"] == transition

    def test_execute_workflow_step_with_various_transitions(self):
        """Test that execute_workflow_step handles various transition types."""
        from app.tasks.workflow import execute_workflow_step

        workflow_id = str(uuid.uuid4())
        transitions = [
            "start",
            "complete_planning",
            "begin_implementation",
            "request_approval",
            "approve",
            "reject",
            "complete",
        ]

        for transition in transitions:
            result = execute_workflow_step(workflow_id, transition)
            assert result["transition"] == transition


class TestHandleApprovalResponseTask:
    """Tests for the handle_approval_response task."""

    def test_handle_approval_response_task_exists(self):
        """Test that handle_approval_response task is registered."""
        import app.tasks.workflow  # noqa: F401
        from app.celery_app import celery_app

        assert "app.tasks.workflow.handle_approval_response" in celery_app.tasks

    def test_handle_approval_response_is_bound_task(self):
        """Test that handle_approval_response is a bound task."""
        from app.tasks.workflow import handle_approval_response

        assert handle_approval_response.__bound__ is True

    def test_handle_approval_response_returns_expected_structure(self):
        """Test that handle_approval_response returns expected result."""
        from app.tasks.workflow import handle_approval_response

        workflow_id = str(uuid.uuid4())
        approved = True
        comment = "LGTM! Proceeding with deployment."

        result = handle_approval_response(workflow_id, approved, comment)

        assert isinstance(result, dict)
        assert "status" in result
        assert "workflow_id" in result
        assert "approved" in result
        assert result["workflow_id"] == workflow_id
        assert result["approved"] == approved

    def test_handle_approval_response_with_rejection(self):
        """Test that handle_approval_response handles rejection."""
        from app.tasks.workflow import handle_approval_response

        workflow_id = str(uuid.uuid4())

        result = handle_approval_response(
            workflow_id, approved=False, comment="Needs more test coverage"
        )

        assert result["approved"] is False

    def test_handle_approval_response_without_comment(self):
        """Test that handle_approval_response handles missing comment."""
        from app.tasks.workflow import handle_approval_response

        workflow_id = str(uuid.uuid4())

        result = handle_approval_response(workflow_id, approved=True)

        assert result["status"] == "pending"


class TestStartSprintWorkflowTask:
    """Tests for the start_sprint_workflow task."""

    def test_start_sprint_workflow_task_exists(self):
        """Test that start_sprint_workflow task is registered."""
        import app.tasks.workflow  # noqa: F401
        from app.celery_app import celery_app

        assert "app.tasks.workflow.start_sprint_workflow" in celery_app.tasks

    def test_start_sprint_workflow_is_bound_task(self):
        """Test that start_sprint_workflow is a bound task."""
        from app.tasks.workflow import start_sprint_workflow

        assert start_sprint_workflow.__bound__ is True

    def test_start_sprint_workflow_returns_expected_structure(self):
        """Test that start_sprint_workflow returns expected result."""
        from app.tasks.workflow import start_sprint_workflow

        project_id = str(uuid.uuid4())
        sprint_id = str(uuid.uuid4())

        result = start_sprint_workflow(project_id, sprint_id)

        assert isinstance(result, dict)
        assert "status" in result
        assert "sprint_id" in result
        assert result["sprint_id"] == sprint_id


class TestStartStoryWorkflowTask:
    """Tests for the start_story_workflow task."""

    def test_start_story_workflow_task_exists(self):
        """Test that start_story_workflow task is registered."""
        import app.tasks.workflow  # noqa: F401
        from app.celery_app import celery_app

        assert "app.tasks.workflow.start_story_workflow" in celery_app.tasks

    def test_start_story_workflow_is_bound_task(self):
        """Test that start_story_workflow is a bound task."""
        from app.tasks.workflow import start_story_workflow

        assert start_story_workflow.__bound__ is True

    def test_start_story_workflow_returns_expected_structure(self):
        """Test that start_story_workflow returns expected result."""
        from app.tasks.workflow import start_story_workflow

        project_id = str(uuid.uuid4())
        story_id = str(uuid.uuid4())

        result = start_story_workflow(project_id, story_id)

        assert isinstance(result, dict)
        assert "status" in result
        assert "story_id" in result
        assert result["story_id"] == story_id


class TestWorkflowTaskRouting:
    """Tests for workflow task queue routing."""

    def test_workflow_tasks_route_to_default_queue(self):
        """Test that workflow tasks route to 'default' queue.

        Per the routing configuration, workflow tasks match 'app.tasks.*'
        which routes to the default queue.
        """
        from app.celery_app import celery_app

        routes = celery_app.conf.task_routes

        # Workflow tasks match the generic 'app.tasks.*' pattern
        # since there's no specific 'app.tasks.workflow.*' route
        assert "app.tasks.*" in routes
        assert routes["app.tasks.*"]["queue"] == "default"

    def test_all_workflow_tasks_match_routing_pattern(self):
        """Test that all workflow task names match the routing pattern."""
        task_names = [
            "app.tasks.workflow.recover_stale_workflows",
            "app.tasks.workflow.execute_workflow_step",
            "app.tasks.workflow.handle_approval_response",
            "app.tasks.workflow.start_sprint_workflow",
            "app.tasks.workflow.start_story_workflow",
        ]

        for name in task_names:
            assert name.startswith("app.tasks.")


class TestWorkflowTaskLogging:
    """Tests for workflow task logging behavior."""

    def test_recover_stale_workflows_logs_execution(self):
        """Test that recover_stale_workflows logs when executed."""
        from app.tasks.workflow import recover_stale_workflows

        with patch("app.tasks.workflow.logger") as mock_logger:
            recover_stale_workflows()

            mock_logger.info.assert_called_once()
            call_args = mock_logger.info.call_args[0][0]
            assert "stale" in call_args.lower() or "recover" in call_args.lower()

    def test_execute_workflow_step_logs_execution(self):
        """Test that execute_workflow_step logs when executed."""
        from app.tasks.workflow import execute_workflow_step

        workflow_id = str(uuid.uuid4())
        transition = "start_planning"

        with patch("app.tasks.workflow.logger") as mock_logger:
            execute_workflow_step(workflow_id, transition)

            mock_logger.info.assert_called_once()
            call_args = mock_logger.info.call_args[0][0]
            assert transition in call_args
            assert workflow_id in call_args

    def test_handle_approval_response_logs_execution(self):
        """Test that handle_approval_response logs when executed."""
        from app.tasks.workflow import handle_approval_response

        workflow_id = str(uuid.uuid4())

        with patch("app.tasks.workflow.logger") as mock_logger:
            handle_approval_response(workflow_id, approved=True)

            mock_logger.info.assert_called_once()
            call_args = mock_logger.info.call_args[0][0]
            assert workflow_id in call_args

    def test_start_sprint_workflow_logs_execution(self):
        """Test that start_sprint_workflow logs when executed."""
        from app.tasks.workflow import start_sprint_workflow

        project_id = str(uuid.uuid4())
        sprint_id = str(uuid.uuid4())

        with patch("app.tasks.workflow.logger") as mock_logger:
            start_sprint_workflow(project_id, sprint_id)

            mock_logger.info.assert_called_once()
            call_args = mock_logger.info.call_args[0][0]
            assert sprint_id in call_args


class TestWorkflowTaskSignatures:
    """Tests for workflow task signature creation."""

    def test_execute_workflow_step_signature_creation(self):
        """Test that execute_workflow_step signature can be created."""
        from app.tasks.workflow import execute_workflow_step

        workflow_id = str(uuid.uuid4())
        transition = "approve"

        sig = execute_workflow_step.s(workflow_id, transition)

        assert sig is not None
        assert sig.args == (workflow_id, transition)

    def test_workflow_chain_creation(self):
        """Test that workflow tasks can be chained together."""
        from celery import chain

        from app.tasks.workflow import (
            start_sprint_workflow,
        )

        project_id = str(uuid.uuid4())
        sprint_id = str(uuid.uuid4())
        str(uuid.uuid4())

        # Build a chain (doesn't execute, just creates the workflow)
        workflow = chain(
            start_sprint_workflow.s(project_id, sprint_id),
            # In reality, these would use results from previous tasks
        )

        assert workflow is not None
