"""QA Engineer skill — test matrix and Apex test validation."""

from __future__ import annotations

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

from packages.artifacts.design import DesignArtifact
from packages.artifacts.qa_report import QAReportArtifact
from packages.artifacts.requirements import RequirementsArtifact
from packages.db.artifacts import ArtifactStore
from packages.integrations.llm import LLMClient, get_llm_client
from packages.integrations.salesforce import ApexTestResult


class QAEngineer:
    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 run_qa(
        self,
        *,
        requirements: RequirementsArtifact,
        design: DesignArtifact,
        file_map: Dict[str, str],
        apex_result: ApexTestResult,
        run_id: UUID,
    ) -> QAReportArtifact:
        force_app_files = {
            p: c for p, c in file_map.items() if p.startswith("force-app/")
        }
        prompt = build_qa_prompt(
            requirements=requirements,
            design=design,
            file_map=force_app_files,
            apex_result=apex_result,
        )
        artifact = await self.llm.generate_qa_report(prompt)
        artifact.jira_key = requirements.jira_key
        artifact.apex_tests_run = apex_result.tests_run
        artifact.apex_tests_passed = apex_result.tests_passed
        artifact.apex_failures = list(apex_result.failures)

        has_apex = any(p.endswith(".cls") for p in force_app_files)
        if apex_result.failures:
            artifact.passed = False
            artifact.summary = f"QA failed — {len(apex_result.failures)} Apex test failure(s)"
        elif apex_result.tests_run > 0 and apex_result.tests_passed < apex_result.tests_run:
            artifact.passed = False
            artifact.summary = artifact.summary or "QA failed — Apex tests did not all pass"
        elif not artifact.test_matrix and requirements.acceptance_criteria:
            artifact.passed = False
            artifact.summary = artifact.summary or "QA failed — no test scenarios derived"
        elif not has_apex and not apex_result.failures:
            # Metadata-only (fields/rules/layouts): mock Apex returns 0 tests — that is not a failure.
            artifact.passed = True
            artifact.summary = (
                "QA passed — acceptance criteria covered; no Apex classes in this change"
            )
        elif not artifact.passed:
            artifact.summary = artifact.summary or "QA failed — mandatory coverage gaps"

        self.artifacts.write_json(run_id, "qa-report.json", artifact)
        return artifact


def build_qa_prompt(
    *,
    requirements: RequirementsArtifact,
    design: DesignArtifact,
    file_map: Dict[str, str],
    apex_result: ApexTestResult,
) -> str:
    ac = "\n".join(f"- {a}" for a in requirements.acceptance_criteria) or "(none)"
    plan = "\n".join(
        f"- {i.metadata_type} {i.api_name}" for i in design.metadata_plan
    ) or "(none)"
    failures = "\n".join(f"- {f}" for f in apex_result.failures) or "(none)"
    file_list = "\n".join(f"- {p}" for p in sorted(file_map)) or "(none)"

    return f"""You are the QA Engineer for Agent Nova (Salesforce development automation).
Derive positive, negative, and manual test scenarios from acceptance criteria.

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

Acceptance criteria:
{ac}

Metadata plan:
{plan}

Generated files:
{file_list}

Apex test results:
tests_run: {apex_result.tests_run}
tests_passed: {apex_result.tests_passed}
failures:
{failures}

passed rules:
- Set passed=true when Apex failures are empty AND test_matrix covers the acceptance criteria.
- tests_run=0 is OK when Generated files have no Apex classes (.cls) — this is metadata-only work.
- Do NOT fail solely because Apex tests were not run / tests_run is 0 for metadata-only changes.
- Set passed=false only for Apex failures, incomplete AC coverage, or missing test scenarios.

For each test_matrix item, type MUST be lowercase exactly one of: positive, negative, manual.
Include at least one positive and one negative scenario per acceptance criterion when possible.
"""
