#!/usr/bin/env python3
"""CLI entry point for code-reviewer

Usage:
    python main.py --repo owner/repository --pr 123 --platform github --review
    python main.py --repo owner/repository --pr 123 --platform github --summary
    python main.py --repo owner/repository --pr 123 --platform github --review --summary
"""

import argparse
import asyncio
import json
import os
import sys
import time

# Fix imports when running as script (not as module)
if __name__ == "__main__" and __package__ is None:
    # Add parent directory to path so we can import code_reviewer
    _parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, _parent)
    __package__ = "code_reviewer"

from rich.console import Console

from code_reviewer import __version__
from code_reviewer.adaptors.factory import create_platform_handlers
from code_reviewer.config import Config, Platform
from code_reviewer.config_file import _run_interactive_setup, load_config_file, validate_config_file
from code_reviewer.errors import (
    ErrorType,
    NotFound,
    PlatformError,
    ReviewateError,
    error_type_for_exception,
)
from code_reviewer.explorer.builder import build_code_explorer
from code_reviewer.logging_config import BaseLogger, setup_logging
from code_reviewer.output import ProgressTracker, emit_error, emit_result, print_result_summary
from code_reviewer.utils import format_diff_with_line_numbers
from code_reviewer.workflows import run_summary_workflow
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", "main")


def parse_args() -> argparse.Namespace:
    """Parse command-line arguments"""
    parser = argparse.ArgumentParser(
        description="Reviewate - AI-powered code review for GitHub and GitLab",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )

    parser.add_argument(
        "--repo",
        required=True,
        help="Repository in format 'owner/repo' or 'group/project'",
    )

    parser.add_argument(
        "--pr",
        required=True,
        help="Pull request or merge request ID/number",
    )

    parser.add_argument(
        "--platform",
        required=True,
        choices=["github", "gitlab"],
        help="Git platform (github or gitlab)",
    )

    parser.add_argument(
        "--review",
        action="store_true",
        help="Run code review workflow",
    )

    parser.add_argument(
        "--summary",
        action="store_true",
        help="Run summary generation workflow",
    )

    parser.add_argument(
        "--json",
        action="store_true",
        help="Output results as JSON (for benchmarking)",
    )

    parser.add_argument(
        "--disable-thinking",
        action="store_true",
        help="Disable extended thinking/reasoning (for non-thinking models)",
    )

    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Run review without posting to platform (for benchmarking)",
    )

    parser.add_argument(
        "--debug",
        action="store_true",
        help="Enable debug mode: fact checker explains reasons, reviews are logged",
    )

    parser.add_argument(
        "--review-model",
        help="Model for review agents (format: provider/model, e.g., anthropic/claude-sonnet-4)",
    )

    parser.add_argument(
        "--utility-model",
        help="Model for utility agents (format: provider/model, e.g., gemini/gemini-2.5-flash-lite)",
    )

    return parser.parse_args()


def cli_entry() -> None:
    """Sync entry point for `pip install reviewate` console script."""
    sys.exit(asyncio.run(main()))


async def main() -> int:
    """Main entry point for the CLI"""
    args = parse_args()
    start_time = time.time()

    # Setup logging: containers set LOG_LEVEL explicitly; TTY defaults to WARNING
    # so the spinner is the primary progress indicator instead of log spam.
    is_tty = sys.stderr.isatty()
    log_level = os.getenv("LOG_LEVEL", "WARNING" if is_tty else "INFO")
    setup_logging(log_level, debug=args.debug)

    # Set global flag for disabling thinking (checked by base agent)
    if args.disable_thinking:
        os.environ["DISABLE_THINKING"] = "1"
        logger.info("Extended thinking disabled (non-thinking model mode)")

    try:
        logger.info("=" * 60)
        logger.info("REVIEWATE_START")
        logger.info("=" * 60)
        logger.info(
            f"Starting Reviewate CLI - repo={args.repo} pr={args.pr} platform={args.platform}"
        )

        # Load config file (interactive setup on first run if needed)
        file_config = load_config_file()
        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.[/]")
                return 1

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

        # Load configuration from environment with optional CLI model overrides
        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))
            return 1
        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))
            return 1

        logger.debug(f"Configuration loaded: platform={args.platform} repo={args.repo}")

        # Create platform adaptors
        repository, issue_handler = create_platform_handlers(args.platform, config)

        if not args.repo or not args.pr:
            raise ValueError("Both --repo and --pr arguments are required")

        # Default to review if no workflow flags specified
        if not args.review and not args.summary:
            args.review = True
            logger.debug("No workflow specified, defaulting to review workflow")

        # Determine platform
        platform = Platform.GITHUB if args.platform == "github" else Platform.GITLAB

        # Parse linked repositories from env var (set by container backend)
        linked_repos_input: list[LinkedRepoInfo] | None = None
        linked_repos_env = os.getenv("LINKED_REPOS")
        if linked_repos_env:
            try:
                linked_repos_input = json.loads(linked_repos_env)
                logger.info(f"Loaded {len(linked_repos_input)} linked repos from env")
            except json.JSONDecodeError:
                logger.warning("Failed to parse LINKED_REPOS env var")

        # Parse team guidelines from env var (set by container backend)
        team_guidelines = os.getenv("TEAM_GUIDELINES")
        if team_guidelines:
            logger.info("Loaded team guidelines from env")

        results: list[tuple[str, WorkflowResult | SummaryResult]] = []
        workflows_run: list[str] = []

        # Progress tracker for TTY mode (spinner + step log)
        tracker: ProgressTracker | None = None
        _exit_error: str | None = None

        on_status = lambda msg: None  # noqa: E731
        on_step_done = lambda detail: None  # noqa: E731

        if is_tty:
            workflows_label = " + ".join(
                f for f, enabled in [("review", args.review), ("summary", args.summary)] if enabled
            )
            review_provider = config.get_agent_provider("review_agent")
            utility_provider = config.get_agent_provider("style_agent")
            tracker = ProgressTracker(
                repo=args.repo,
                pr=args.pr,
                workflows=workflows_label,
                review_model=f"{review_provider}/{config.get_agent_model('review_agent')}",
                utility_model=f"{utility_provider}/{config.get_agent_model('style_agent')}",
                version=__version__,
            )
            tracker.start()
            on_status = tracker.step
            on_step_done = tracker.done

        try:
            # Run review workflow if requested
            if args.review:
                workflows_run.append("review")
                logger.info("Starting review workflow")
                on_status("Fetching PR...")
                # Fetch merge request
                try:
                    merge_request = await repository.fetch_merge_request(args.repo, args.pr)
                except NotFound:
                    _exit_error = f"Pull request #{args.pr} not found in {args.repo}"
                    emit_error(ErrorType.NOT_FOUND, _exit_error)
                    return 1
                except PlatformError as e:
                    _exit_error = str(e)
                    emit_error(error_type_for_exception(e), _exit_error)
                    return 1
                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 ""

                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:
                    _exit_error = (
                        f"Branch '{merge_request.source_branch}' not found in {args.repo}"
                        " — it may have been deleted"
                    )
                    emit_error(ErrorType.NOT_FOUND, _exit_error)
                    return 1
                except PlatformError as e:
                    _exit_error = str(e)
                    emit_error(error_type_for_exception(e), _exit_error)
                    return 1

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

                workflow = ReviewWorkflow(config)

                if args.dry_run:
                    review_result = 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:
                    review_result = 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,
                    )

                results.append(("Review", review_result))

            # Run summary workflow if requested
            if args.summary:
                workflows_run.append("summary")
                logger.info("Starting summary workflow")
                on_status("Running summary workflow...")

                summary_result = await run_summary_workflow(
                    config=config,
                    repository=repository,
                    issue_handler=issue_handler,
                    repo=args.repo,
                    merge_id=args.pr,
                    dry_run=args.dry_run,
                    on_status=on_status,
                    on_step_done=on_step_done,
                )
                results.append(("Summary", summary_result))
        finally:
            if tracker is not None:
                if _exit_error:
                    tracker.fail(_exit_error)
                else:
                    tracker.finish()

        # Build result data 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,
                    }
                )

        result_data: dict[str, object] = {
            "status": "success",
            "workflows": workflows_list,
            "token_usage": token_usage,
            "per_agent_usage": per_agent_usage,
            "review_comments": review_comments,
            "review_objects": review_objects,
        }

        print_result_summary(result_data)
        emit_result(result_data, include_reviews=args.dry_run)
        return 0

    except KeyboardInterrupt:
        total_duration = time.time() - start_time
        logger.warning(f"REVIEWATE_INTERRUPTED user_cancelled=true duration={total_duration:.2f}s")
        emit_error(ErrorType.INTERRUPTED, "User cancelled the operation")
        return 1
    except Exception as e:
        total_duration = time.time() - start_time
        logger.error(
            f"REVIEWATE_ERROR error_type={type(e).__name__} message={str(e)} duration={total_duration:.2f}s"
        )
        logger.exception("Full traceback:")
        emit_error(error_type_for_exception(e), str(e))
        return 1


if __name__ == "__main__":
    cli_entry()
