import json
from pathlib import Path
import structlog

from app.configs import settings
from app.state import EngineeringState

logger = structlog.get_logger()


class ReviewerAgent:
    """
    Reviewer Agent
    职责：代码 Review、Bug 检测、安全检查、性能检查
    """

    SYSTEM_PROMPT = """You are a Senior Code Reviewer.
Review the code changes and provide structured feedback.

Output a JSON object with:
{
  "score": 0-100,
  "approved": true/false,
  "bugs": ["description of bug 1", ...],
  "security": ["security issue 1", ...],
  "performance": ["performance issue 1", ...],
  "style": ["style issue 1", ...],
  "suggestions": ["improvement suggestion 1", ...],
  "summary": "Overall review summary"
}

Be thorough but constructive. Focus on:
- Logic errors and bugs
- SQL injection, XSS, auth issues
- N+1 queries, memory leaks, inefficient algorithms
- Code style and maintainability
"""

    def __init__(self):
        self.logger = structlog.get_logger().bind(agent="reviewer")
        self._setup_llm()

    def _setup_llm(self):
        try:
            import anthropic
            self.client = anthropic.Anthropic(api_key=settings.anthropic_api_key)
            self.model = "claude-opus-4-5"
        except ImportError:
            self.client = None

    def review(self, state: EngineeringState) -> dict:
        """执行代码 Review"""
        self.logger.info("Reviewer starting review")

        code_output = state.get("code_result", {}).get("output", "")
        sandbox_path = state.get("sandbox_path", "")
        diff = state.get("diff_result", {})

        # Read changed files for review
        code_context = self._read_changed_files(
            sandbox_path,
            diff.get("modified", []) + diff.get("added", []),
        )

        if not self.client:
            return self._static_review(code_context, state)

        prompt = f"""## Code Review Request

### User Request
{state.get('user_request', '')}

### Changed Files
{json.dumps(diff.get('modified', []) + diff.get('added', []), indent=2)}

### Code Changes
```
{code_context[:8000]}
```

### Test Results
Passed: {state.get('test_result', {}).get('passed', 'N/A')}
Failed: {state.get('test_result', {}).get('failed', 'N/A')}
Coverage: {state.get('test_result', {}).get('coverage', 'N/A')}

Review the code and provide structured feedback."""

        try:
            response = self.client.messages.create(
                model=self.model,
                max_tokens=4096,
                system=self.SYSTEM_PROMPT,
                messages=[{"role": "user", "content": prompt}],
            )
            text = response.content[0].text
            start = text.find("{")
            end = text.rfind("}") + 1
            if start >= 0:
                return json.loads(text[start:end])
        except Exception as e:
            self.logger.warning("Reviewer LLM call failed", error=str(e))

        return self._static_review(code_context, state)

    def _read_changed_files(self, sandbox_path: str, files: list, max_chars: int = 8000) -> str:
        if not sandbox_path or not files:
            return "No code changes available"
        root = Path(sandbox_path)
        content_parts = []
        total = 0
        for f in files[:10]:
            path = root / f
            if path.exists() and path.is_file():
                try:
                    text = path.read_text(encoding="utf-8", errors="ignore")
                    part = f"\n### {f}\n```\n{text}\n```"
                    if total + len(part) > max_chars:
                        break
                    content_parts.append(part)
                    total += len(part)
                except Exception:
                    pass
        return "\n".join(content_parts) if content_parts else "No readable files"

    def _static_review(self, code_context: str, state: EngineeringState) -> dict:
        """静态分析（无 LLM 时的回退）"""
        issues = []

        # Basic checks
        if "eval(" in code_context:
            issues.append("Potential security risk: eval() usage detected")
        if "exec(" in code_context:
            issues.append("Potential security risk: exec() usage detected")
        if "TODO" in code_context or "FIXME" in code_context:
            issues.append("Unresolved TODO/FIXME comments found")
        if "print(" in code_context and "debug" not in code_context.lower():
            issues.append("Debug print() statements found - use logging instead")

        tests = state.get("test_result", {})
        approved = tests.get("failed", 0) == 0 and len(issues) == 0

        return {
            "score": 70 if approved else 50,
            "approved": approved,
            "bugs": [],
            "security": [i for i in issues if "security" in i.lower()],
            "performance": [],
            "style": [i for i in issues if "security" not in i.lower()],
            "suggestions": ["Add more test coverage", "Add docstrings"],
            "summary": "Static review completed (LLM not available)",
        }

    def needs_fix(self, review_result: dict) -> bool:
        """判断是否需要修复"""
        return (
            not review_result.get("approved", True)
            or len(review_result.get("bugs", [])) > 0
            or len(review_result.get("security", [])) > 0
        )
