"""Single registry for public final-report task contracts."""
from __future__ import annotations


CURRENT_REPORT_SCHEMA_VERSION = "3.0"
LATEST_REPORT_SCHEMA_VERSION = "3.0"

PUBLIC_REPORT_TASK_TYPES = (
    "requirements-discovery",
    "improvement-discovery",
    "error-analysis",
    "implementation-option-selection",
    "project-analysis",
    "feature-analysis",
    "change-impact-analysis",
    "implementation-planning",
    "implementation",
    "final-verification",
    "release-handoff",
)

TASK_TYPE_DATA_PROPERTY = {
    "requirements-discovery": "requirementsDiscovery",
    "improvement-discovery": "improvementDiscovery",
    "error-analysis": "errorAnalysis",
    "implementation-option-selection": "implementationOptionSelection",
    "project-analysis": "projectAnalysis",
    "feature-analysis": "featureAnalysis",
    "change-impact-analysis": "changeImpactAnalysis",
    "implementation-planning": "implementationPlanning",
    "implementation": "implementation",
    "final-verification": "finalVerification",
    "release-handoff": "releaseHandoff",
}

TASK_TYPE_HTML_TEMPLATE = {
    task_type: f"html/tasks/{task_type}.template.html"
    for task_type in PUBLIC_REPORT_TASK_TYPES
}

# The AI handoff Markdown's task body, sibling of the human HTML template above.
# Both are keyed off the same task-type list so a new task type cannot ship one
# audience's view without the other.
TASK_TYPE_MARKDOWN_TEMPLATE = {
    task_type: f"md/tasks/{task_type}.template.md"
    for task_type in PUBLIC_REPORT_TASK_TYPES
}

TASK_TYPE_REQUIRED_HUMAN_FIELDS = {
    "requirements-discovery": (
        "requirementsDiscovery.requestVerbatim",
        "requirementsDiscovery.systemInterpretation",
        "requirementsDiscovery.classification",
        "requirementsDiscovery.confirmedRequirements",
        "requirementsDiscovery.unresolvedRequirements",
    ),
    "improvement-discovery": (
        "improvementDiscovery.candidates",
        "improvementDiscovery.lensCoverage",
    ),
    "error-analysis": (
        "errorAnalysis.symptomVerbatim",
        "errorAnalysis.observableFailure",
        "errorAnalysis.causeCandidates.confidence",
        "errorAnalysis.causeCandidates.falsifyingEvidenceChecked",
        "errorAnalysis.causeCandidates.disproveWith",
    ),
    "implementation-option-selection": (
        "implementationOptionSelection.decisionContext.originalRequirementIds",
        "implementationOptionSelection.rankedOptions.requirementCoverage",
        "implementationOptionSelection.rankedOptions.coverageSummary",
        "implementationOptionSelection.recommendedOptionId",
    ),
    "project-analysis": (
        "projectAnalysis.components",
        "projectAnalysis.entryPoints",
        "projectAnalysis.dataStores",
        "projectAnalysis.externalSystems",
        "projectAnalysis.rankedHotspots",
        "projectAnalysis.qualityCoverage",
        "projectAnalysis.featureIndex",
    ),
    "feature-analysis": (
        "featureAnalysis.flows",
        "featureAnalysis.domainRules",
        "featureAnalysis.stateChanges",
        "featureAnalysis.externalInteractions",
        "featureAnalysis.testCoverage",
    ),
    "change-impact-analysis": (
        "changeImpactAnalysis.impactItems",
        "changeImpactAnalysis.dependencyBlastRadius",
        "changeImpactAnalysis.unaffectedBoundaries",
        "changeImpactAnalysis.compatibilityImpact",
        "changeImpactAnalysis.migrationImpact",
        "changeImpactAnalysis.rollbackImpact",
        "changeImpactAnalysis.securityAndPerformanceImpact",
        "changeImpactAnalysis.planningInputs",
    ),
    # Legacy plans render option comparison fields; selected-direction plans
    # render the selected reference, its realization, and exact coverage. The
    # task-level marker is the only field common to both HTML branches. Each
    # branch's required details are covered by its schema and renderer tests.
    "implementation-planning": (
        "implementationPlanning",
    ),
    "implementation": (
        "implementation.diffSummary",
        "implementation.requirementCoverage",
        "implementation.validationEvidence",
        "implementation.manualUserTest",
    ),
    "final-verification": (
        "finalVerification.validationEvidence",
        "finalVerification.addedSurfaceAudit",
        "finalVerification.acceptanceBlockers",
        "finalVerification.residualRisk",
        "finalVerification.manualUserTest",
    ),
    "release-handoff": (
        "releaseHandoff.userSelections",
        "releaseHandoff.featureBranchState",
        "releaseHandoff.pullRequestOutcome",
        "releaseHandoff.mergeConflictProbe",
    ),
}

IMPLEMENTATION_PLANNING_LEGACY_REQUIRED_HUMAN_FIELDS = (
    "implementationPlanning.optionCandidates",
    "implementationPlanning.tradeoffMatrix",
    "implementationPlanning.recommendedOption",
    "implementationPlanning.stageMap",
    "implementationPlanning.planBodyVerification",
)

IMPLEMENTATION_PLANNING_PLAN_READY_REQUIRED_HUMAN_FIELDS = (
    "implementationPlanning.selectedDirectionRef",
    "implementationPlanning.directionRealization",
    "implementationPlanning.coverageSummary",
    "implementationPlanning.stageMap",
    "implementationPlanning.requirementCoverage",
    "implementationPlanning.planBodyVerification",
)

IMPLEMENTATION_PLANNING_INVALIDATED_REQUIRED_HUMAN_FIELDS = (
    "implementationPlanning.selectedDirectionRef",
    "implementationPlanning.directionInvalidation",
    "implementationPlanning.routing",
)


def required_human_fields_for_report(task_type: str, data: dict) -> tuple[str, ...]:
    if task_type != "implementation-planning":
        return TASK_TYPE_REQUIRED_HUMAN_FIELDS.get(task_type, ())
    planning = data.get("implementationPlanning", {})
    if planning.get("planningContract") != "selected-direction":
        return IMPLEMENTATION_PLANNING_LEGACY_REQUIRED_HUMAN_FIELDS
    if planning.get("outcome") == "direction-invalidated":
        return IMPLEMENTATION_PLANNING_INVALIDATED_REQUIRED_HUMAN_FIELDS
    return IMPLEMENTATION_PLANNING_PLAN_READY_REQUIRED_HUMAN_FIELDS


def report_property_for(task_type: str) -> str:
    try:
        return TASK_TYPE_DATA_PROPERTY[task_type]
    except KeyError as exc:
        raise ValueError(f"unsupported public report task type: {task_type}") from exc


def html_template_for(task_type: str) -> str:
    try:
        return TASK_TYPE_HTML_TEMPLATE[task_type]
    except KeyError as exc:
        raise ValueError(f"no task-specific html template for: {task_type}") from exc


def execution_roles_from_manifest(manifest: dict) -> list[dict]:
    """Copy the exact RoleExecution set. Do not invent rows."""
    if int(manifest.get("executionIdentityVersion") or 0) != 2:
        return []
    roles = []
    for row in manifest.get("roleExecutions") or []:
        if not isinstance(row, dict):
            continue
        roles.append({
            "roleExecutionRef": row.get("roleExecutionRef"),
            "role": row.get("role"),
            "provider": row.get("provider"),
            "modelRef": row.get("modelRef"),
            "ordinal": row.get("ordinal"),
            "executionLabel": row.get("executionLabel"),
            "status": row.get("status"),
        })
    return roles


def apply_execution_roles(data: dict, manifest: dict) -> dict:
    """Copy the manifest RoleExecution set onto a final-report payload."""
    updated = dict(data)
    if int(manifest.get("executionIdentityVersion") or 0) != 2:
        return updated
    updated["executionIdentityVersion"] = 2
    updated["executionRoles"] = execution_roles_from_manifest(manifest)
    return updated


def assert_execution_roles_match_manifest(report: dict, manifest: dict) -> None:
    report_refs = {row.get("roleExecutionRef") for row in report.get("executionRoles") or []}
    manifest_refs = {
        row.get("roleExecutionRef") for row in manifest.get("roleExecutions") or []
    }
    if report_refs != manifest_refs:
        raise ValueError("report executionRoles must equal manifest roleExecutions")


def markdown_template_for(task_type: str) -> str:
    try:
        return TASK_TYPE_MARKDOWN_TEMPLATE[task_type]
    except KeyError as exc:
        raise ValueError(f"no task-specific markdown template for: {task_type}") from exc
