"""Config validation, review workflow execution, and result aggregation.

Used by main.py to keep the CLI entry point slim.
"""

from __future__ import annotations

import argparse
import asyncio
import os
import sys
from collections.abc import Callable

from rich.console import Console

from code_reviewer.adaptors import IssueHandler, RepositoryHandler
from code_reviewer.config import Config, Platform
from code_reviewer.config_file import _run_interactive_setup, validate_config_file
from code_reviewer.errors import (
    CLIError,
    ErrorType,
    NotFound,
    PlatformError,
    ReviewateError,
    error_type_for_exception,
)
from code_reviewer.explorer.builder import build_code_explorer
from code_reviewer.logging_config import BaseLogger
from code_reviewer.output import emit_error
from code_reviewer.utils import format_diff_with_line_numbers
from code_reviewer.workflows.context import LinkedRepoInfo
from code_reviewer.workflows.review import ReviewWorkflow, WorkflowResult
from code_reviewer.workflows.summary import SummaryResult

# Initialize module logger wrapper
logger = BaseLogger("main", "runner")


def _load_and_validate_config(
    args: argparse.Namespace,
    file_config: dict | None,
) -> tuple[Config, str, str]:
    """Load config, validate, and verify API keys.

    Returns (config, review_provider, utility_provider).
    Raises SystemExit(1) on validation or API key failure.
    """
    has_model_env = os.getenv("REVIEW_AGENT_MODEL") or os.getenv("UTILITY_AGENT_MODEL")
    has_model_cli = args.review_model or args.utility_model

    if file_config and not has_model_env and not has_model_cli:
        errors = validate_config_file(file_config)
        if errors:
            _err_console = Console(stderr=True, highlight=False)
            _err_console.print("[bold red]Invalid config file (~/.reviewate/config.toml):[/]")
            for err in errors:
                _err_console.print(f"  [red]{err}[/]")
            _err_console.print("[dim]Fix the file or delete it to re-run setup.[/]")
            raise SystemExit(1) from None

    if not file_config and not has_model_env and not has_model_cli and sys.stdin.isatty():
        file_config = _run_interactive_setup()

    config = Config.from_env(
        review_model=args.review_model,
        utility_model=args.utility_model,
        file_config=file_config,
    )
    if args.review_model:
        logger.info(f"Using CLI override for review model: {args.review_model}")
    if args.utility_model:
        logger.info(f"Using CLI override for utility model: {args.utility_model}")

    # Early check: verify API keys are set for all providers in use
    try:
        review_provider = config.get_agent_provider("review_agent")
        config.get_api_key(review_provider)
    except ReviewateError as e:
        logger.error(str(e))
        raise SystemExit(1) from None
    try:
        utility_provider = config.get_agent_provider("style_agent")
        if utility_provider != review_provider:
            config.get_api_key(utility_provider)
    except ReviewateError as e:
        logger.error(str(e))
        raise SystemExit(1) from None

    return config, review_provider, utility_provider


async def _run_review_workflow(
    *,
    args: argparse.Namespace,
    config: Config,
    repository: RepositoryHandler,
    issue_handler: IssueHandler,
    platform: Platform,
    linked_repos_input: list[LinkedRepoInfo] | None,
    team_guidelines: str | None,
    on_status: Callable[[str], None] | None,
    on_step_done: Callable[[str], None] | None,
) -> WorkflowResult:
    """Fetch PR, build code explorer, and run the review workflow.

    Raises CLIError on handled failures (error already emitted to user).
    """
    if on_status:
        on_status("Fetching PR...")

    try:
        merge_request = await repository.fetch_merge_request(args.repo, args.pr)
    except NotFound:
        msg = f"Pull request #{args.pr} not found in {args.repo}"
        emit_error(ErrorType.NOT_FOUND, msg)
        raise CLIError(msg) from None
    except PlatformError as e:
        emit_error(error_type_for_exception(e), str(e))
        raise CLIError(str(e)) from None

    merge_request.diffs = format_diff_with_line_numbers(merge_request.diffs)

    # Fetch user guidelines and build code explorer in parallel
    async def _fetch_guidelines() -> str:
        for filename in ["CLAUDE.md", "AGENTS.md"]:
            content = await repository.fetch_file(args.repo, merge_request.target_branch, filename)
            if content:
                logger.info(f"Found user guidelines in {filename}")
                return content
        logger.info("No user guidelines found (CLAUDE.md or AGENTS.md)")
        return ""

    if on_status:
        on_status("Building code explorer...")

    try:
        user_guidelines, (code_explorer, linked_repos_info) = await asyncio.gather(
            _fetch_guidelines(),
            build_code_explorer(
                config,
                repository,
                args.repo,
                merge_request.source_branch,
                linked_repos=linked_repos_input,
            ),
        )
    except NotFound:
        msg = (
            f"Branch '{merge_request.source_branch}' not found in {args.repo}"
            " — it may have been deleted"
        )
        emit_error(ErrorType.NOT_FOUND, msg)
        raise CLIError(msg) from None
    except PlatformError as e:
        emit_error(error_type_for_exception(e), str(e))
        raise CLIError(str(e)) from None

    if code_explorer is None:
        logger.error("Failed to build code explorer — cannot proceed with review")
        msg = "Failed to build code explorer from source branch"
        emit_error(ErrorType.EXPLORER_ERROR, msg)
        raise CLIError(msg) from None

    workflow = ReviewWorkflow(config)

    if args.dry_run:
        return await workflow.review(
            merge_request=merge_request,
            platform=platform,
            code_explorer=code_explorer,
            user_guidelines=user_guidelines,
            issue_handler=issue_handler,
            repository=repository,
            debug=args.debug,
            linked_repos=linked_repos_info,
            team_guidelines=team_guidelines,
            on_status=on_status,
            on_step_done=on_step_done,
        )
    else:
        return await workflow.run(
            merge_request=merge_request,
            platform=platform,
            code_explorer=code_explorer,
            repository=repository,
            issue_handler=issue_handler,
            user_guidelines=user_guidelines,
            debug=args.debug,
            linked_repos=linked_repos_info,
            team_guidelines=team_guidelines,
            on_status=on_status,
            on_step_done=on_step_done,
        )


def _aggregate_results(
    results: list[tuple[str, WorkflowResult | SummaryResult]],
) -> dict[str, object]:
    """Build result data dict from workflow results."""
    token_usage = {
        "input_tokens": 0,
        "output_tokens": 0,
        "total_tokens": 0,
        "cached_tokens": 0,
    }
    per_agent_usage: dict[str, dict] = {}
    review_comments: list[str] = []
    review_objects: list[dict] = []
    workflows_list: list[dict] = []

    for _name, result in results:
        if result.total_usage:
            usage = result.total_usage
            token_usage["input_tokens"] += usage.input_tokens
            token_usage["output_tokens"] += usage.output_tokens
            token_usage["total_tokens"] += usage.total_tokens
            token_usage["cached_tokens"] += usage.cached_tokens

        if isinstance(result, WorkflowResult):
            per_agent_usage.update(result.per_agent_usage or {})
            review_comments.extend(result.review_comments or [])
            review_objects.extend(result.review_objects or [])
            workflows_list.append(
                {
                    "name": "review",
                    "issues_found": len(result.issues_found) if result.issues_found else 0,
                }
            )
        elif isinstance(result, SummaryResult):
            workflows_list.append(
                {
                    "name": "summary",
                    "summary_length": len(result.summary_body) if result.summary_body else 0,
                }
            )

    return {
        "status": "success",
        "workflows": workflows_list,
        "token_usage": token_usage,
        "per_agent_usage": per_agent_usage,
        "review_comments": review_comments,
        "review_objects": review_objects,
    }
