"""Main orchestrator for performance mode analysis.

Pipeline:
1. Load staged diff or use provided file list
2. Filter Python files
3. AST analysis to detect performance bottlenecks
4. Score and filter top suspicious regions
5. LLM review of top regions
"""

import logging
import os
from pathlib import Path
from typing import Any, Dict, List, Optional

from dotenv import load_dotenv

from performance.review.analyzer import analyze_performance_bottlenecks, filter_top_suspicious
from performance.git.git import get_diff, get_repo_root, GitError
from performance.git.preprocess import get_filtered_files_from_diff
from performance.review.reviewer import review_performance_issues

load_dotenv()
logger = logging.getLogger(__name__)


def run_performance_mode_analysis(
    review_scope: str = "staged",
    file_list: Optional[List[Path]] = None,
    top_percent: int = 10,
    progress_handle=None,
) -> Dict[str, Any]:
    """Run performance mode analysis pipeline.
    
    Pipeline:
    1. Load staged diff or use provided file list
    2. Filter Python files
    3. AST analysis to detect performance bottlenecks
    4. Score and filter top suspicious regions
    5. LLM review of top regions
    
    Args:
        review_scope: Review scope selection ("staged" or "all")
        file_list: Files to review when review_scope is "all"
        top_percent: Percentage of suspicious regions to review with LLM (default 10%)
        
    Returns:
        dict with analysis results
    """
    # Input validation
    if review_scope not in ("staged", "all"):
        return _result(
            status='invalid_input',
            message=f'Invalid review_scope: {review_scope}.',
        )
    
    if not isinstance(top_percent, int) or top_percent <= 0 or top_percent > 100:
        logger.error(f"Invalid top_percent value: {top_percent}")
        return _result(
            status='invalid_input',
            message=f'Invalid top_percent: {top_percent}. Must be between 1 and 100.',
        )
    
    # Check environment variables
    model_url = os.getenv("GAR_LLM_BASE_URL")
    if not model_url:
        logger.error("GAR_LLM_BASE_URL environment variable not set")
        return _result(
            status='config_error',
            message='GAR_LLM_BASE_URL not configured. Please set GAR_LLM_BASE_URL in .env file.',
        )
    
    try:
        existing_files: List[str] = []
        
        if review_scope == "staged":
            # 1. Load staged diff
            logger.debug("Loading staged diff...")
            try:
                diff = get_diff(staged=True, color=False)
            except GitError as e:
                logger.error(f"Git operation failed: {e}")
                return _result(
                    status='git_error',
                    message=f'Git error: {str(e)}. Make sure you are in a git repository.',
                )
            
            if not diff:
                return _result(
                    status='no_changes',
                    message='No staged changes found.',
                )
            
            # 2. Filter files from diff
            logger.debug("Filtering diff sections...")
            try:
                filtered_files = get_filtered_files_from_diff(diff)
                logger.debug(f"Filtered files: {filtered_files}")
            except Exception as e:
                logger.error(f"Failed to filter diff: {e}")
                return _result(
                    status='filter_error',
                    message=f'Failed to process diff: {str(e)}',
                )
            
            if not filtered_files:
                return _result(
                    status='all_filtered',
                    message='All files were filtered out (binary/build/minified).',
                )
            
            # 3. Get absolute paths (Python files only)
            try:
                repo_root = Path(get_repo_root())
            except (GitError, Exception) as e:
                logger.warning(f"Could not get repo root: {e}. Using current directory.")
                repo_root = Path.cwd()
            
            logger.debug(f"Repo root: {repo_root}")
            
            ## 실제 경로에 해당 .py 파일 존재하는지 확인 -> existing_files 에 추가
            for f in filtered_files:
                try:
                    abs_path = repo_root / f
                    if abs_path.exists() and abs_path.suffix == ".py":
                        existing_files.append(str(abs_path))
                    else:
                        logger.debug(f"File not found or not Python: {abs_path}")
                except (OSError, ValueError) as e:
                    logger.warning(f"Invalid file path '{f}': {e}")
                    continue
        else:
            # review_scope == "all"
            logger.debug("Collecting all project files...")
            if file_list:
                # 특정 파일들만 분석
                for f in file_list:
                    try:
                        abs_path = Path(f).resolve()
                        if abs_path.exists() and abs_path.suffix == ".py":
                            existing_files.append(str(abs_path))
                        else:
                            logger.debug(f"File not found or not Python: {abs_path}")
                    except (OSError, ValueError) as e:
                        logger.warning(f"Invalid file path '{f}': {e}")
                        continue
            else:
                # 전체 프로젝트 스캔
                try:
                    from common.file_collector import FileCollector, FileCollectorConfig
                    
                    repo_root = Path(get_repo_root())
                    config = FileCollectorConfig(
                        extensions=frozenset({".py"}),
                        include_empty=True,
                    )
                    collector = FileCollector(config)
                    result = collector.collect_from_root(root=repo_root)
                    
                    for path in result.valid_files:
                        existing_files.append(str(path))
                    
                    logger.debug(f"Collected {len(existing_files)} Python files from project root")
                except Exception as e:
                    logger.error(f"Failed to collect project files: {e}")
                    return _result(
                        status='collection_error',
                        message=f'Failed to collect project files: {str(e)}',
                    )
        
        if not existing_files:
            return _result(
                status='no_existing',
                message='No Python files found to analyze.',
            )
        
        logger.debug(f"Found {len(existing_files)} Python files to analyze")
        
        # 4. AST analysis (1단계: 빠른 스캔)
        logger.debug("AST 분석 중...")
        try:
            all_suspicious = analyze_performance_bottlenecks(files=[Path(f) for f in existing_files])
        except Exception as e:
            logger.error(f"AST analysis failed: {e}")
            return _result(
                status='analysis_error',
                message=f'AST analysis failed: {str(e)}',
            )
        
        if not all_suspicious:
            return _result(
                status='no_issues',
                message='No performance bottlenecks detected.',
                files_analyzed=existing_files,
                suspicious_regions=[],
                findings=[],
            )
        
        # 5. Filter top suspicious regions
        top_suspicious = filter_top_suspicious(all_suspicious, top_percent=top_percent)
        logger.debug(f"AST 분석 완료: {len(all_suspicious)}개 의심 구간 발견 → 상위 {len(top_suspicious)}개 선택 (상위 {top_percent}%)")
        
        if not top_suspicious:
            return _result(
                status='no_top_issues',
                message='상위 의심 구간이 없습니다.',
                files_analyzed=existing_files,
                suspicious_regions=all_suspicious,
                findings=[],
            )
        
        # 6. LLM review (2단계: 정밀 검토)
        try:
            findings = review_performance_issues(
                suspicious_regions=top_suspicious,
                file_entries=None,  # 파일 직접 읽기 사용
                progress_handle=progress_handle,
            )
        except Exception as e:
            logger.error(f"LLM review failed: {e}")
            return _result(
                status='review_error',
                message=f'LLM review failed: {str(e)}',
                files_analyzed=existing_files,
                suspicious_regions=all_suspicious,
                findings=[],
            )
        
        # 7. Build result
        return {
            'status': 'success',
            'message': f'Analysis complete. {len(all_suspicious)} suspicious regions found, {len(top_suspicious)} reviewed.',
            'files_analyzed': existing_files,
            'suspicious_regions': all_suspicious,
            'reviewed_regions': top_suspicious,
            'findings': findings,
        }
        
    except KeyboardInterrupt:
        logger.info("Analysis interrupted by user")
        return _result(
            status='interrupted',
            message='Analysis interrupted by user.',
        )
    except Exception as e:
        logger.error(f"Unexpected error during analysis: {e}", exc_info=True)
        return _result(
            status='error',
            message=f'Unexpected error: {str(e)}',
        )


def run_performance_mode_from_input_context(
    input_context: Dict[str, Any],
    top_percent: int = 10,
    progress_handle=None,
) -> Dict[str, Any]:
    """Run performance mode using InputContext (staged files from input_context).
    
    Args:
        input_context: Input context with file entries
        top_percent: Percentage of suspicious regions to review with LLM
    
    Returns:
        dict with analysis results
    """
    if not input_context:
        return _result(status='invalid_input', message='InputContext is empty.')
    
    file_entries = input_context.get("files", [])
    if not file_entries:
        return _result(status='no_changes', message='No staged changes found.')
    
    # Python 파일만 필터링
    python_entries = [e for e in file_entries if e.get("path", "").endswith(".py")]
    
    if not python_entries:
        return _result(status='no_python_files', message='No Python files found.')
    
    try:
        # 1. AST analysis
        logger.debug("AST 분석 중...")
        all_suspicious = analyze_performance_bottlenecks(file_entries=python_entries)
        
        if not all_suspicious:
            return _result(
                status='no_issues',
                message='No performance bottlenecks detected.',
                files_analyzed=[e.get("path") for e in python_entries],
                suspicious_regions=[],
                findings=[],
            )
        
        # 2. Filter top suspicious regions
        top_suspicious = filter_top_suspicious(all_suspicious, top_percent=top_percent)
        logger.debug(f"AST 분석 완료: {len(all_suspicious)}개 의심 구간 발견 → 상위 {len(top_suspicious)}개 선택 (상위 {top_percent}%)")
        
        if not top_suspicious:
            return _result(
                status='no_top_issues',
                message='상위 의심 구간이 없습니다.',
                files_analyzed=[e.get("path") for e in python_entries],
                suspicious_regions=all_suspicious,
                findings=[],
            )
        
        # 3. LLM review
        findings = review_performance_issues(
            suspicious_regions=top_suspicious,
            file_entries=python_entries,
            progress_handle=progress_handle,
        )
        
        return {
            'status': 'success',
            'message': f'Analysis complete. {len(all_suspicious)} suspicious regions found, {len(top_suspicious)} reviewed.',
            'files_analyzed': [e.get("path") for e in python_entries],
            'suspicious_regions': all_suspicious,
            'reviewed_regions': top_suspicious,
            'findings': findings,
        }
    except Exception as e:
        logger.error(f"Review process failed: {e}")
        return _result(status='review_error', message=f'Code review failed: {str(e)}')


def _result(status: str, message: str, **kwargs) -> Dict[str, Any]:
    """Create a result dict with default values."""
    return {
        'status': status,
        'message': message,
        'files_analyzed': [],
        'suspicious_regions': [],
        'reviewed_regions': [],
        'findings': [],
        **kwargs,
    }
