"""SARIF 2.1.0 structure generation for audit findings."""

from typing import Any

from tools.generate_report.models import AuditLens
from tools.workflow.state import WorkflowState

SARIF_SCHEMA = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
SARIF_VERSION = "2.1.0"
OPTIX_VERSION = "0.1.0"
OPTIX_INFO_URI = "https://github.com/ravnhq/optix"

SEVERITY_TO_LEVEL: dict[str, str] = {
    "critical": "error",
    "high": "error",
    "medium": "warning",
    "low": "note",
    "info": "note",
}

LENS_TO_RULE_PREFIX: dict[AuditLens, str] = {
    AuditLens.SECURITY: "SEC",
    AuditLens.A11Y: "A11Y",
    AuditLens.PRINCIPAL: "PRIN",
    AuditLens.DEVOPS: "DEVOPS",
}


class SarifGenerator:
    """Generates SARIF 2.1.0 compliant output from workflow state."""

    def __init__(self, state: WorkflowState, lens: AuditLens) -> None:
        self._state = state
        self._lens = lens
        self._rules: dict[str, dict[str, Any]] = {}

    def generate(self) -> dict[str, Any]:
        """Generate complete SARIF structure."""
        results = self._build_results()
        rules = list(self._rules.values())

        return {
            "$schema": SARIF_SCHEMA,
            "version": SARIF_VERSION,
            "runs": [
                {
                    "tool": {
                        "driver": {
                            "name": "Optix",
                            "version": OPTIX_VERSION,
                            "informationUri": OPTIX_INFO_URI,
                            "rules": rules,
                        }
                    },
                    "results": results,
                }
            ],
        }

    def _build_results(self) -> list[dict[str, Any]]:
        """Build SARIF results array from issues_found."""
        results = []
        if not self._state.consolidated:
            return results

        for issue in self._state.consolidated.issues_found:
            result = self._convert_issue_to_result(issue)
            if result:
                results.append(result)

        return results

    def _convert_issue_to_result(self, issue: dict[str, Any]) -> dict[str, Any] | None:
        """Convert a single issue to SARIF result format."""
        description = issue.get("description", "")
        if not description:
            return None

        severity = issue.get("severity", "medium").lower()
        category = issue.get("category", "general")
        rule_id = self._get_or_create_rule(issue, category, severity)

        result: dict[str, Any] = {
            "ruleId": rule_id,
            "level": SEVERITY_TO_LEVEL.get(severity, "warning"),
            "message": {"text": description},
        }

        locations = self._build_locations(issue)
        if locations:
            result["locations"] = locations

        finding_id = issue.get("finding_id")
        if finding_id:
            result["fingerprints"] = {"optix/finding_id": finding_id}

        return result

    def _get_or_create_rule(
        self, issue: dict[str, Any], category: str, severity: str
    ) -> str:
        """Get existing rule or create new one, returning rule ID."""
        cwe_id = issue.get("cwe_id")
        wcag_criterion = issue.get("wcag_criterion")

        if cwe_id:
            rule_id = cwe_id
        elif wcag_criterion:
            rule_id = f"WCAG-{wcag_criterion}"
        else:
            prefix = LENS_TO_RULE_PREFIX.get(self._lens, "OPX")
            rule_id = f"{prefix}-{category.upper().replace(' ', '_')}"

        if rule_id not in self._rules:
            rule: dict[str, Any] = {
                "id": rule_id,
                "shortDescription": {"text": category},
            }

            if cwe_id:
                rule["helpUri"] = f"https://cwe.mitre.org/data/definitions/{cwe_id.replace('CWE-', '')}.html"

            if wcag_criterion:
                rule["helpUri"] = f"https://www.w3.org/WAI/WCAG21/Understanding/{wcag_criterion.replace('.', '')}"

            default_severity = SEVERITY_TO_LEVEL.get(severity, "warning")
            rule["defaultConfiguration"] = {"level": default_severity}

            self._rules[rule_id] = rule

        return rule_id

    def _build_locations(self, issue: dict[str, Any]) -> list[dict[str, Any]]:
        """Build SARIF locations array from affected files."""
        locations = []
        affected_files = issue.get("affected_files", [])

        if not affected_files:
            file_path = issue.get("file_path") or issue.get("location")
            if file_path:
                affected_files = [file_path]

        for file_info in affected_files:
            location = self._build_single_location(file_info)
            if location:
                locations.append(location)

        return locations

    def _build_single_location(
        self, file_info: str | dict[str, Any]
    ) -> dict[str, Any] | None:
        """Build a single SARIF location."""
        if isinstance(file_info, str):
            return {
                "physicalLocation": {
                    "artifactLocation": {"uri": file_info}
                }
            }

        if isinstance(file_info, dict):
            file_path = file_info.get("file_path", "")
            if not file_path:
                return None

            physical_location: dict[str, Any] = {
                "artifactLocation": {"uri": file_path}
            }

            region = self._build_region(file_info)
            if region:
                physical_location["region"] = region

            return {"physicalLocation": physical_location}

        return None

    def _build_region(self, file_info: dict[str, Any]) -> dict[str, Any] | None:
        """Build SARIF region from line number info."""
        line_start = file_info.get("line_start") or file_info.get("line_number")
        if not line_start:
            return None

        region: dict[str, int] = {"startLine": int(line_start)}

        line_end = file_info.get("line_end")
        if line_end:
            region["endLine"] = int(line_end)

        return region

    def get_rules_count(self) -> int:
        """Return the count of unique rules generated."""
        return len(self._rules)
