"""Phase 4 — Code Reviewer, QA, static analysis, and fix loop tests."""

from __future__ import annotations

import uuid

import pytest

from packages.agents.code_reviewer import CodeReviewer
from packages.agents.qa_engineer import QAEngineer
from packages.agents.salesforce_developer import SalesforceDeveloper
from packages.artifacts.design import DesignArtifact, MetadataPlanItem
from packages.artifacts.implementation import ImplementationArtifact
from packages.artifacts.requirements import RequirementsArtifact
from packages.artifacts.review import ReviewArtifact, ReviewFinding
from packages.config import Settings
from packages.db.artifacts import ArtifactStore
from packages.integrations.salesforce import ApexTestResult, get_apex_test_runner
from packages.integrations.static_analysis import (
    MockStaticAnalyzer,
    StaticAnalysisResult,
    StaticViolation,
    get_static_analyzer,
)
from packages.integrations.llm import MockLLMClient


@pytest.mark.asyncio
async def test_code_reviewer_flags_insecure_apex(artifacts_tmpdir):
    run_id = uuid.uuid4()
    requirements = RequirementsArtifact(
        jira_key="SALES-301",
        summary="Add handler",
        acceptance_criteria=["Handler runs safely"],
        is_clear=True,
        confidence=0.9,
    )
    design = DesignArtifact(
        jira_key="SALES-301",
        summary="Apex handler",
        approach="Apex",
        metadata_plan=[
            MetadataPlanItem(
                metadata_type="ApexClass",
                api_name="AccountHandler",
                action="create",
                object="Account",
            )
        ],
    )
    dev = SalesforceDeveloper()
    impl, file_map = await dev.implement(design, requirements, run_id)

    reviewer = CodeReviewer()
    review = await reviewer.review(
        requirements=requirements,
        design=design,
        implementation=impl,
        file_map=file_map,
        static_analysis=StaticAnalysisResult(success=True, violations=[]),
        cycle=0,
        run_id=run_id,
    )

    assert review.passed is False
    assert any(f.category == "security" for f in review.findings)
    assert any(f.category == "test_coverage" for f in review.findings)


@pytest.mark.asyncio
async def test_static_violations_merged_into_review(artifacts_tmpdir):
    run_id = uuid.uuid4()
    requirements = RequirementsArtifact(
        jira_key="SALES-302",
        summary="Field",
        acceptance_criteria=["Field exists"],
        is_clear=True,
        confidence=0.9,
    )
    design = DesignArtifact(
        jira_key="SALES-302",
        summary="Field",
        approach="Config",
        metadata_plan=[
            MetadataPlanItem(
                metadata_type="CustomField",
                api_name="Tier__c",
                action="create",
                object="Account",
            )
        ],
    )
    dev = SalesforceDeveloper()
    impl, file_map = await dev.implement(design, requirements, run_id)

    static = StaticAnalysisResult(
        success=True,
        violations=[
            StaticViolation(
                rule="AvoidHardcoding",
                file="force-app/main/default/classes/Bad.cls",
                line=3,
                message="Hard-coded ID",
                severity="high",
            )
        ],
    )
    review = await CodeReviewer().review(
        requirements=requirements,
        design=design,
        implementation=impl,
        file_map=file_map,
        static_analysis=static,
        cycle=0,
        run_id=run_id,
    )
    assert any(f.category == "static_analysis" for f in review.findings)


@pytest.mark.asyncio
async def test_apply_fixes_adds_test_class(artifacts_tmpdir):
    run_id = uuid.uuid4()
    requirements = RequirementsArtifact(
        jira_key="SALES-303",
        summary="Handler",
        acceptance_criteria=["Works"],
        is_clear=True,
        confidence=0.9,
    )
    design = DesignArtifact(
        jira_key="SALES-303",
        summary="Handler",
        approach="Apex",
        metadata_plan=[
            MetadataPlanItem(
                metadata_type="ApexClass",
                api_name="AccountHandler",
                action="create",
                object="Account",
            )
        ],
    )
    dev = SalesforceDeveloper()
    impl, file_map = await dev.implement(design, requirements, run_id)
    store = ArtifactStore()
    store.write_json(run_id, "implementation.json", impl)

    review = ReviewArtifact(
        jira_key="SALES-303",
        passed=False,
        cycle=0,
        findings=[
            ReviewFinding(
                severity="high",
                category="test_coverage",
                file="force-app/main/default/classes/AccountHandler.cls",
                message="Missing @IsTest companion class",
                fixable=True,
            )
        ],
        summary="Missing tests",
        static_analysis_status="passed",
    )

    updated_impl, updated_files = await dev.apply_fixes(
        review=review,
        design=design,
        requirements=requirements,
        run_id=run_id,
        file_map=file_map,
    )

    assert any("AccountHandlerTest.cls" in p for p in updated_files)
    assert "with sharing" in updated_files["force-app/main/default/classes/AccountHandler.cls"]
    assert "fix cycle" in (updated_impl.notes or "").lower()


@pytest.mark.asyncio
async def test_qa_engineer_builds_test_matrix(artifacts_tmpdir):
    run_id = uuid.uuid4()
    requirements = RequirementsArtifact(
        jira_key="SALES-304",
        summary="Tier field",
        acceptance_criteria=["Gold requires revenue", "Field visible to sales"],
        is_clear=True,
        confidence=0.9,
    )
    design = DesignArtifact(
        jira_key="SALES-304",
        summary="Tier field",
        approach="Config",
        metadata_plan=[
            MetadataPlanItem(
                metadata_type="CustomField",
                api_name="Tier__c",
                action="create",
                object="Account",
            )
        ],
    )
    dev = SalesforceDeveloper()
    _, file_map = await dev.implement(design, requirements, run_id)

    report = await QAEngineer().run_qa(
        requirements=requirements,
        design=design,
        file_map=file_map,
        apex_result=ApexTestResult(success=True, tests_run=0, tests_passed=0),
        run_id=run_id,
    )

    assert report.passed is True
    assert len(report.test_matrix) >= 2
    assert any(s.type == "positive" for s in report.test_matrix)
    assert any(s.type == "negative" for s in report.test_matrix)


def test_get_static_analyzer_mock():
    settings = Settings(static_analysis_provider="mock")
    analyzer = get_static_analyzer(settings)
    assert isinstance(analyzer, MockStaticAnalyzer)


def test_get_apex_test_runner_mock():
    settings = Settings(apex_test_provider="mock")
    runner = get_apex_test_runner(settings)
    assert runner is not None


@pytest.mark.asyncio
async def test_mock_llm_review_and_fix_roundtrip():
    llm = MockLLMClient()
    prompt = """Jira key: SALES-305
Review cycle: 0
Metadata plan:
- ApexClass AccountHandler on Account
Generated files:
### force-app/main/default/classes/AccountHandler.cls
```
public without sharing class AccountHandler {
    public static void run() {}
}
```
Static analysis violations:
(none)
"""
    review = await llm.review_code(prompt)
    assert review.passed is False

    fix_prompt = """Findings to address:
- [high/test_coverage] force-app/main/default/classes/AccountHandler.cls: Missing @IsTest
Files to fix:
### force-app/main/default/classes/AccountHandler.cls
```
public without sharing class AccountHandler {
    public static void run() {}
}
```
"""
    patches = await llm.fix_code(fix_prompt)
    assert "with sharing" in patches["force-app/main/default/classes/AccountHandler.cls"]
    assert any("Test.cls" in p for p in patches)
