"""Code Reviewer skill — review generated SFDX against requirements and design."""

from __future__ import annotations

from typing import Dict, List, Optional
from uuid import UUID

from packages.artifacts.design import DesignArtifact
from packages.artifacts.implementation import ImplementationArtifact
from packages.artifacts.requirements import RequirementsArtifact
from packages.artifacts.review import BLOCKING_SEVERITIES, ReviewArtifact, ReviewFinding
from packages.db.artifacts import ArtifactStore
from packages.integrations.llm import LLMClient, get_llm_client
from packages.integrations.static_analysis import StaticAnalysisResult


class CodeReviewer:
    def __init__(
        self,
        llm: Optional[LLMClient] = None,
        artifacts: Optional[ArtifactStore] = None,
    ) -> None:
        self.llm = llm or get_llm_client()
        self.artifacts = artifacts or ArtifactStore()

    async def review(
        self,
        *,
        requirements: RequirementsArtifact,
        design: DesignArtifact,
        implementation: ImplementationArtifact,
        file_map: Dict[str, str],
        static_analysis: StaticAnalysisResult,
        cycle: int,
        run_id: UUID,
    ) -> ReviewArtifact:
        force_app_files = {
            p: c for p, c in file_map.items() if p.startswith("force-app/")
        }
        prompt = build_review_prompt(
            requirements=requirements,
            design=design,
            implementation=implementation,
            file_map=force_app_files,
            static_analysis=static_analysis,
            cycle=cycle,
        )
        artifact = await self.llm.review_code(prompt)
        artifact.jira_key = requirements.jira_key
        artifact.cycle = cycle

        pmd_findings = _static_violations_to_findings(static_analysis)
        existing = {(f.file, f.message) for f in artifact.findings}
        for f in pmd_findings:
            if (f.file, f.message) not in existing:
                artifact.findings.append(f)

        blocking = [f for f in artifact.findings if f.severity in BLOCKING_SEVERITIES]
        artifact.passed = len(blocking) == 0
        if static_analysis.violations:
            artifact.static_analysis_status = "failed"
        elif static_analysis.success:
            artifact.static_analysis_status = "passed"
        else:
            artifact.static_analysis_status = "skipped"

        if not artifact.passed and not artifact.summary:
            artifact.summary = f"Code review failed with {len(blocking)} blocking finding(s)"

        cycle_path = f"review-cycle-{cycle}.json"
        self.artifacts.write_json(run_id, cycle_path, artifact)
        self.artifacts.write_json(run_id, "review.json", artifact)
        return artifact


def build_review_prompt(
    *,
    requirements: RequirementsArtifact,
    design: DesignArtifact,
    implementation: ImplementationArtifact,
    file_map: Dict[str, str],
    static_analysis: StaticAnalysisResult,
    cycle: int,
) -> str:
    ac = "\n".join(f"- {a}" for a in requirements.acceptance_criteria) or "(none)"
    plan = "\n".join(
        f"- {i.metadata_type} {i.api_name} on {i.object or 'N/A'}"
        for i in design.metadata_plan
    ) or "(none)"
    files_section = _format_files(file_map)
    static_lines = (
        "\n".join(
            f"- [{v.severity}] {v.file}:{v.line or '?'} {v.rule}: {v.message}"
            for v in static_analysis.violations
        )
        or "(none)"
    )

    return f"""You are the Code Reviewer for Agent Nova (Salesforce development automation).
Review generated SFDX metadata against requirements, design, and Salesforce best practices.

Jira key: {requirements.jira_key}
Review cycle: {cycle}
Summary: {requirements.summary}

Acceptance criteria:
{ac}

Metadata plan:
{plan}

Implementation summary: {implementation.summary}

Checklist: security (with sharing, CRUD/FLS), bulkification, naming, test coverage, design drift.

Static analysis violations:
{static_lines}

Generated files:
{files_section}

Severity for each finding MUST be lowercase exactly one of: critical, high, medium, low, info.
Return blocking severity only for critical/high issues. Medium/low are informational.
passed=true only when there are zero critical/high findings.
"""


def _format_files(file_map: Dict[str, str], max_chars: int = 4000) -> str:
    if not file_map:
        return "(none)"
    parts: List[str] = []
    for path, content in sorted(file_map.items()):
        snippet = content if len(content) <= max_chars else content[:max_chars] + "\n... truncated"
        parts.append(f"### {path}\n```\n{snippet}\n```")
    return "\n\n".join(parts)


def _static_violations_to_findings(static_analysis: StaticAnalysisResult) -> List[ReviewFinding]:
    findings: List[ReviewFinding] = []
    for v in static_analysis.violations:
        sev = "high" if str(v.severity) in ("1", "2", "high", "critical") else "medium"
        findings.append(
            ReviewFinding(
                severity=sev,  # type: ignore[arg-type]
                category="static_analysis",
                file=v.file,
                line=v.line,
                message=f"{v.rule}: {v.message}",
                fixable=True,
            )
        )
    return findings
