from __future__ import annotations

import pytest
from pydantic import ValidationError

from stirling.agents import UserSpecAgent
from stirling.contracts import (
    AgentDraft,
    AgentDraftRequest,
    AgentRevisionRequest,
    ConversationMessage,
    EditClarificationRequest,
    EditPlanResponse,
    ToolOperationStep,
)
from stirling.models.tool_models import Angle, FlattenParams, RotatePdfParams, ToolEndpoint
from stirling.services.runtime import AppRuntime


class StubUserSpecAgent(UserSpecAgent):
    def __init__(self, runtime: AppRuntime, draft_result: AgentDraft, revision_result: AgentDraft) -> None:
        super().__init__(runtime)
        self.draft_result = draft_result
        self.revision_result = revision_result
        self.edit_plan = EditPlanResponse(
            summary="Rotate the document.",
            steps=[
                ToolOperationStep(
                    tool=ToolEndpoint.ROTATE_PDF,
                    parameters=RotatePdfParams(angle=Angle(90)),
                )
            ],
        )

    async def _build_edit_plan(
        self, user_message: str, conversation_history: list[ConversationMessage]
    ) -> EditPlanResponse:
        return self.edit_plan

    async def _run_draft_agent(self, request: AgentDraftRequest, edit_plan: EditPlanResponse) -> AgentDraft:
        return self.draft_result

    async def _run_revision_agent(self, request: AgentRevisionRequest, edit_plan: EditPlanResponse) -> AgentDraft:
        return self.revision_result


class ClarifyingUserSpecAgent(UserSpecAgent):
    def __init__(self, runtime: AppRuntime) -> None:
        super().__init__(runtime)

    async def _build_edit_plan(
        self, user_message: str, conversation_history: list[ConversationMessage]
    ) -> EditClarificationRequest:
        return EditClarificationRequest(
            question="Which pages should be changed?",
            reason="The request does not specify the target pages.",
        )


@pytest.mark.anyio
async def test_user_spec_agent_drafts_agent_spec(runtime: AppRuntime) -> None:
    agent = StubUserSpecAgent(
        runtime,
        AgentDraft(
            name="Invoice Cleanup",
            description="Prepare invoices for review.",
            objective="Normalize invoices before accounting review.",
            steps=[
                ToolOperationStep(
                    tool=ToolEndpoint.ROTATE_PDF,
                    parameters=RotatePdfParams(angle=Angle(90)),
                )
            ],
        ),
        revision_result=AgentDraft(
            name="Unused",
            description="Unused",
            objective="Unused",
            steps=[],
        ),
    )

    response = await agent.draft(
        AgentDraftRequest(
            user_message="Build me an invoice cleanup agent.",
            conversation_history=[
                ConversationMessage(role="user", content="It should handle scanned PDFs."),
            ],
        )
    )

    assert response.outcome == "draft"
    assert response.draft.name == "Invoice Cleanup"
    assert response.draft.steps[0].kind == "tool"


@pytest.mark.anyio
async def test_user_spec_agent_revises_existing_draft(runtime: AppRuntime) -> None:
    current_draft = AgentDraft(
        name="Invoice Cleanup",
        description="Prepare invoices for review.",
        objective="Normalize invoices before accounting review.",
        steps=[
            ToolOperationStep(
                tool=ToolEndpoint.ROTATE_PDF,
                parameters=RotatePdfParams(angle=Angle(90)),
            )
        ],
    )
    agent = StubUserSpecAgent(
        runtime,
        draft_result=current_draft,
        revision_result=AgentDraft(
            name="Invoice Cleanup",
            description="Prepare invoices for review and reduce file size.",
            objective="Normalize invoices before accounting review.",
            steps=[
                ToolOperationStep(
                    tool=ToolEndpoint.ROTATE_PDF,
                    parameters=RotatePdfParams(angle=Angle(90)),
                ),
                ToolOperationStep(
                    tool=ToolEndpoint.FLATTEN,
                    parameters=FlattenParams(flatten_only_forms=False, render_dpi=None),
                ),
            ],
        ),
    )

    response = await agent.revise(
        AgentRevisionRequest(
            user_message="Also compress the files before upload.",
            current_draft=current_draft,
        )
    )

    assert response.outcome == "draft"
    assert len(response.draft.steps) == 2
    assert response.draft.steps[1].kind == "tool"


def test_tool_operation_step_rejects_mismatched_parameters() -> None:
    with pytest.raises(ValidationError):
        ToolOperationStep(
            tool=ToolEndpoint.ROTATE_PDF,
            parameters=FlattenParams(flatten_only_forms=False, render_dpi=None),
        )


@pytest.mark.anyio
async def test_user_spec_agent_propagates_edit_clarification(runtime: AppRuntime) -> None:
    agent = ClarifyingUserSpecAgent(runtime)

    response = await agent.draft(AgentDraftRequest(user_message="Build an agent to rotate some pages."))

    assert isinstance(response, EditClarificationRequest)
