"""Review Agents

Ensemble review agents that run in parallel for better coverage.
"""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Annotated, Any

from pydantic import BaseModel, ConfigDict, Field

from ..schema import (
    BasicReviewOutput,
    GitHubReviewComment,
    GitLabReviewComment,
    Issue,
    MergeRequest,
    ReviewOutput,
)
from .base import AgentConfig, AgentResponse, BaseAgentImpl, ReasoningEffort
from .output_parser import OutputParserAgent
from .utils import format_template

if TYPE_CHECKING:
    from ..explorer import CodeExplorer
    from ..workflows.context import LinkedRepoInfo

logger = logging.getLogger(__name__)

# Type alias for response model types
ReviewOutputType = (
    type[ReviewOutput[GitHubReviewComment]]
    | type[ReviewOutput[GitLabReviewComment]]
    | type[ReviewOutput[BasicReviewOutput]]
)


class SimpleReviewAgent:
    """Simple review agent using a non-specialized prompt.

    Used in ensemble mode where multiple instances run in parallel
    to get diverse perspectives on the same code.
    """

    MAX_ITERATIONS = 12  # 10 tool calls + 1 initial + 1 final
    MAX_TOOL_CALLS = 10

    def __init__(
        self,
        config: Annotated[AgentConfig, Field(description="LLM configuration")],
        instance_id: int = 0,
        reasoning_effort: ReasoningEffort = None,
    ):
        self.instance_id = instance_id
        self.base = BaseAgentImpl(
            name=f"Reviewer-{instance_id}",
            description="Generic code reviewer",
            config=config,
            reasoning_effort=reasoning_effort,
        )

    async def analyze(
        self,
        merge_info: MergeRequest,
        diffs: str,
        output_parser: OutputParserAgent,
        response_model: ReviewOutputType,
        code_explorer: CodeExplorer,
        issues: list[Issue] | None = None,
        user_guidelines: str = "",
        linked_repos: list[LinkedRepoInfo] | None = None,
        team_guidelines: str | None = None,
        platform_output_format: str = "",
    ) -> AgentResponse[
        ReviewOutput[GitHubReviewComment]
        | ReviewOutput[GitLabReviewComment]
        | ReviewOutput[BasicReviewOutput]
    ]:
        """Analyze merge request for bugs."""
        from .tools import build_codebase_tools, make_tool_handler

        ctx: dict[str, object] = {
            "title": merge_info.title,
            "description": merge_info.description,
            "target_branch": merge_info.target_branch,
            "source_branch": merge_info.source_branch,
            "project_id": merge_info.repo,
            "user_guidelines": user_guidelines,
            "issues": issues or [],
            "linked_repos": linked_repos or [],
            "team_guidelines": team_guidelines or "",
            "has_tools": True,
            "platform_output_format": platform_output_format,
        }

        # Build project names for grep enum when linked repos exist
        project_names: list[str] | None = None
        if linked_repos:
            project_names = ["primary"] + [r["name"] for r in linked_repos]

        cached_diffs = self.base.format_cached_diff(diffs)
        template = self.base.load_prompt("review_agent.txt")
        system_prompt = format_template(self.base.jinja_env, template, ctx)
        output_format = self.base.get_output_format(response_model)

        tool_handler = make_tool_handler(
            code_explorer, f"Reviewer-{self.instance_id}", self.MAX_TOOL_CALLS
        )
        return await self.base.analyze(
            system_prompt=system_prompt,
            user_prompt=output_format,
            response_model=response_model,
            output_parser=output_parser,
            cached_content=cached_diffs,
            tools=build_codebase_tools(project_names),
            tool_handler=tool_handler,
            max_iterations=self.MAX_ITERATIONS,
        )


class SynthesizerOutput(BaseModel):
    """Output from the synthesizer agent."""

    model_config = ConfigDict(extra="forbid")

    reviews: Annotated[list[dict], Field(description="Merged and deduplicated reviews")]


class SynthesizerAgent:
    """Merges and deduplicates reviews from multiple review agents.

    Combines insights from parallel review agents into a unified,
    prioritized review.
    """

    def __init__(
        self,
        config: Annotated[AgentConfig, Field(description="LLM configuration")],
    ):
        self.base = BaseAgentImpl(
            name="Synthesizer",
            description="Merges reviews from parallel agents",
            config=config,
        )

    async def synthesize(
        self,
        agent_reviews: dict[str, list[Any]],
        merge_info: MergeRequest,
        output_parser: OutputParserAgent,
        response_model: ReviewOutputType,
    ) -> AgentResponse[
        ReviewOutput[GitHubReviewComment]
        | ReviewOutput[GitLabReviewComment]
        | ReviewOutput[BasicReviewOutput]
    ]:
        """Merge and deduplicate reviews from multiple agents.

        Args:
            agent_reviews: Dict mapping agent name to list of reviews
            merge_info: The merge request being reviewed
            output_parser: Output parser for fixing invalid responses
            response_model: Pydantic model class for response

        Returns:
            AgentResponse with merged reviews
        """
        ctx: dict[str, object] = {
            "agent_reviews": agent_reviews,
            "title": merge_info.title,
            "description": merge_info.description,
            "target_branch": merge_info.target_branch,
            "source_branch": merge_info.source_branch,
            "project_id": merge_info.repo,
        }

        template = self.base.load_prompt("synthesizer_agent.txt")
        system_prompt = format_template(self.base.jinja_env, template, ctx)
        output_format = self.base.get_output_format(response_model)

        return await self.base.analyze(
            system_prompt=system_prompt,
            user_prompt=output_format,
            response_model=response_model,
            output_parser=output_parser,
        )
