"""LLM-based reviewer for performance bottlenecks.

Takes suspicious regions detected by AST analyzer and performs
detailed review with LLM considering runtime context.
"""

from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import Any

from performance.ai.ai import generate_review, LLMError
import os

logger = logging.getLogger(__name__)

PERFORMANCE_SYSTEM_PROMPT = """You are a performance analysis expert. Your task is to review code regions that have been flagged by static analysis as potential performance bottlenecks.

Analyze the code considering:
1. Actual data size and scale
2. Execution frequency (hot path vs cold path)
3. Runtime environment differences
4. Cache hit possibilities
5. External system delays (DB, network)

Determine if the flagged issue is:
- A real performance problem that needs fixing
- A false positive (acceptable for the use case)
- A potential issue that depends on runtime conditions

Provide specific, actionable recommendations if it's a real problem."""


def _get_code_context(
    region: dict[str, Any], 
    file_entries: list[dict[str, Any]] | None = None,
    progress_handle=None,
    read_files: set[str] | None = None,
) -> str:
    """Extract code context around the suspicious region."""
    file_path = region["file"]
    func_start_line = region.get("function_context", {}).get("start_line", region["line"])
    func_line_count = region.get("function_context", {}).get("line_count", 50)
    
    # 파일 내용 가져오기
    source = ""
    if file_entries:
        for entry in file_entries:
            if entry.get("path") == file_path:
                source = entry.get("content", "")
                break
    
    if not source:
        # 파일 직접 읽기
        try:
            source = Path(file_path).read_text(encoding="utf-8")
            
            # 파일 읽기 진행률 업데이트 (중복 카운트 방지)
            if progress_handle and read_files is not None:
                if file_path not in read_files:
                    read_files.add(file_path)
                    from common.ui import update_file_read_progress
                    update_file_read_progress(progress_handle)
        except Exception as e:
            logger.warning(f"Could not read {file_path}: {e}")
            return ""
    
    # 함수 코드 추출 (주변 컨텍스트 포함)
    lines = source.splitlines()
    context_start = max(0, func_start_line - 5)
    context_end = min(len(lines), func_start_line + func_line_count + 5)
    
    code_snippet = "\n".join(lines[context_start:context_end])
    return code_snippet


def _parse_review_to_finding(region: dict[str, Any], review_text: str) -> dict[str, Any]:
    """Parse LLM review response to structured finding."""
    import re
    
    finding = {
        "file": region["file"],
        "function": region["function"],
        "line": region["line"],
        "score": region["score"],
        "issues": region["issues"],
        "review": review_text,
        "severity": "medium",  # 기본값
        "message": "",
        "suggestion": "",
    }
    
    # LLM 응답 파싱 (마크다운 형식 지원)
    review_lower = review_text.lower()
    
    # Severity 추출
    severity_match = re.search(r"severity[:\s]*\*?\*?(\w+)\*?\*?", review_lower)
    if severity_match:
        sev = severity_match.group(1)
        if sev in ("critical", "high", "severe"):
            finding["severity"] = "high"
        elif sev in ("low", "minor", "acceptable"):
            finding["severity"] = "low"
        else:
            finding["severity"] = "medium"
    else:
        # 휴리스틱 fallback
        if "critical" in review_lower or "severe" in review_lower:
            finding["severity"] = "high"
        elif "minor" in review_lower or "acceptable" in review_lower or "false positive" in review_lower:
            finding["severity"] = "low"
    
    # Message 추출 (더 간단하고 확실한 방법)
    lines = review_text.split("\n")
    message_lines = []
    skip_next = False
    
    for i, line in enumerate(lines):
        line = line.strip()
        if not line:
            continue
        
        # Severity 라인 스킵
        if re.search(r"severity[:\s]*\*?\*?\w+\*?\*?", line, re.IGNORECASE):
            skip_next = True
            continue
        
        # 구분선 스킵
        if line.startswith("---") or line.startswith("==="):
            continue
        
        # 마크다운 헤더 제거하고 내용 추출
        cleaned = re.sub(r"^#+\s*", "", line)  # 헤더 제거
        cleaned = re.sub(r"\*+([^*]+)\*+", r"\1", cleaned)  # 볼드 제거
        cleaned = cleaned.strip()
        
        if cleaned and len(cleaned) > 10:  # 의미있는 내용만
            message_lines.append(cleaned)
            if len(message_lines) >= 2:  # 2줄이면 충분
                break
    
    if message_lines:
        finding["message"] = " ".join(message_lines)[:250]
    else:
        # Fallback: AST 이슈 설명 사용
        if region.get("issues"):
            first_issue = region["issues"][0]
            finding["message"] = first_issue.get("description", f"성능 병목 가능성 (점수: {region['score']})")
        else:
            finding["message"] = f"성능 병목 가능성 감지 (점수: {region['score']})"
    
    # Suggestion 추출
    suggestion_keywords = ["suggestion", "recommendation", "fix", "improvement", "solution", "해결", "개선"]
    suggestion_lines = []
    
    for i, line in enumerate(lines):
        line_lower = line.lower()
        if any(keyword in line_lower for keyword in suggestion_keywords):
            # 키워드 다음 줄들 수집
            for j in range(i + 1, min(i + 4, len(lines))):
                next_line = lines[j].strip()
                if not next_line or next_line.startswith("---"):
                    continue
                cleaned = re.sub(r"^[-*•]\s*", "", next_line)  # 리스트 마커 제거
                cleaned = re.sub(r"\*+([^*]+)\*+", r"\1", cleaned)  # 볼드 제거
                cleaned = re.sub(r"^#+\s*", "", cleaned)  # 헤더 제거
                cleaned = cleaned.strip()
                if cleaned and len(cleaned) > 10:
                    suggestion_lines.append(cleaned)
                    if len(suggestion_lines) >= 2:
                        break
            break
    
    if suggestion_lines:
        finding["suggestion"] = " ".join(suggestion_lines)[:250]
    else:
        # Fallback: AST 이슈 기반 제안
        if region.get("issues"):
            issue_types = [issue.get("type", "") for issue in region["issues"]]
            if "loop_io" in str(issue_types):
                finding["suggestion"] = "루프 밖으로 I/O 호출을 이동하거나 배치 처리 고려"
            elif "nested_loop" in str(issue_types):
                finding["suggestion"] = "중첩 루프 최적화 또는 알고리즘 개선 검토"
            elif "loop_object" in str(issue_types):
                finding["suggestion"] = "루프 밖에서 객체 생성 후 재사용"
            else:
                finding["suggestion"] = "코드 리뷰 및 성능 프로파일링 권장"
        else:
            finding["suggestion"] = "수동 확인 필요"
    
    return finding


def review_performance_issues(
    suspicious_regions: list[dict[str, Any]],
    file_entries: list[dict[str, Any]] | None = None,
    progress_handle=None,
) -> list[dict[str, Any]]:
    """Review suspicious regions with LLM.
    
    Args:
        suspicious_regions: List of suspicious regions from AST analyzer (이미 필터링된 상위 구간만)
        file_entries: File entries with content (optional)
        progress_handle: Progress handle for file reading progress tracking
    
    Returns:
        List of reviewed findings
    """
    if not suspicious_regions:
        return []
    
    # 고유 파일 개수 계산 (진행률 추적용)
    unique_files = set(region["file"] for region in suspicious_regions)
    read_files: set[str] = set()
    
    # progress handle이 있으면 total 설정
    if progress_handle:
        from common.ui import update_file_read_progress
        update_file_read_progress(progress_handle, total=len(unique_files))
    
    findings = []
    total = len(suspicious_regions)
    logger.debug(f"LLM 상세 분석 시작: {total}개 구간 검토 중...")
    
    for idx, region in enumerate(suspicious_regions, 1):
        logger.debug(f"LLM 분석 중 ({idx}/{total}): {region['file']}:{region['function']}")
        try:
            # 코드 컨텍스트 가져오기
            code_context = _get_code_context(region, file_entries, progress_handle, read_files)
            
            # 이슈 요약
            issues_summary = []
            for issue in region["issues"]:
                issues_summary.append(
                    f"- {issue['type']}: 라인 {issue['line']} ({issue['description']})"
                )
            
            # LLM 프롬프트 구성
            user_prompt = f"""다음 코드 구간에서 성능 병목 가능성을 분석하세요.

파일: {region['file']}
함수: {region['function']}
점수: {region['score']} (의심도)

AST 분석에서 감지된 패턴:
{chr(10).join(issues_summary)}

코드:
```python
{code_context}
```

이 코드가 실제로 성능 문제를 일으킬 수 있는지, 아니면 false positive인지 판단하세요.
실제 데이터 크기, 실행 빈도, 런타임 환경을 고려하여 분석하세요.

응답 형식:
- Severity: high/medium/low
- Message: 문제 설명
- Suggestion: 개선 방안 (문제인 경우)
"""
            
            # LLM 호출
            model = os.getenv("GAR_MODEL", "default")
            review = generate_review(
                model=model,
                prompt=(PERFORMANCE_SYSTEM_PROMPT, user_prompt),
                expect_json=False,  # 마크다운 형식 응답 허용
            )
            
            # 파싱
            finding = _parse_review_to_finding(region, review)
            
            # 디버깅: 파싱 결과 확인
            logger.debug(f"Parsed finding: file={finding['file']}, function={finding['function']}, message={finding['message'][:50]}...")
            
            findings.append(finding)
            
        except LLMError as e:
            logger.warning(f"LLM error reviewing {region['file']}:{region['function']}: {e}")
            # LLM 실패 시 AST 결과만 사용
            finding = {
                "file": region["file"],
                "function": region["function"],
                "line": region["line"],
                "score": region["score"],
                "issues": region["issues"],
                "review": None,
                "severity": "medium",
                "message": f"AST 분석에서 {len(region['issues'])}개 패턴 감지",
                "suggestion": "LLM 검토 실패 - 수동 확인 필요",
            }
            findings.append(finding)
        except Exception as e:
            logger.error(f"Error reviewing {region['file']}:{region['function']}: {e}")
            continue
    
    return findings
