"""SARIF v2.1.0 output for Census compare results.

Converts a CompareReport into the OASIS Static Analysis Results
Interchange Format (SARIF) for ingestion by GitHub Security tab,
VS Code SARIF Viewer, and other compatible tools.
"""

from __future__ import annotations

import copy
from pathlib import PurePosixPath
from typing import Any
from urllib.parse import quote

from .compare import CompareReport


_SARIF_SCHEMA = (
    "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/"
    "main/sarif-2.1/schema/sarif-schema-2.1.0.json"
)

_RULE_ID = "CENSUS001"

_RULE: dict[str, Any] = {
    "id": _RULE_ID,
    "name": "ExfiltrationMatch",
    "shortDescription": {
        "text": "File matches attested inventory",
    },
    "fullDescription": {
        "text": (
            "A suspect file\u2019s SHA-256 hash matches a hash previously "
            "attested in the CertiSigma registry, indicating the file may "
            "have been exfiltrated from the inventoried source."
        ),
    },
    "defaultConfiguration": {"level": "error"},
    "help": {
        "text": (
            "A file in the suspect directory has the same SHA-256 hash as "
            "a file previously attested in your CertiSigma inventory. This "
            "indicates the file content was copied from the inventoried "
            "source — a potential data exfiltration event.\n\n"
            "Review the suspect file path, compare with the inventory path, "
            "and check the attestation timestamp to determine the chain of "
            "custody."
        ),
        "markdown": (
            "A file in the suspect directory has the **same SHA-256 hash** "
            "as a file previously attested in your CertiSigma inventory.\n\n"
            "This indicates the file content was copied from the inventoried "
            "source — a **potential data exfiltration** event.\n\n"
            "**Next steps:**\n"
            "1. Review the suspect file path\n"
            "2. Compare with the inventory path (if available)\n"
            "3. Check the attestation timestamp for chain of custody\n"
            "4. Use `census verify <hash>` for full evidence chain"
        ),
    },
    "helpUri": "https://certisigma.ch/docs/census/compare",
    "properties": {
        "tags": ["security", "data-exfiltration", "file-integrity"],
    },
}


def compare_report_to_sarif(
    report: CompareReport,
    tool_version: str,
    suspect_dir: str,
) -> dict[str, Any]:
    """Convert a CompareReport to SARIF v2.1.0 JSON.

    Parameters
    ----------
    report:
        The result of :func:`compare_directory`.
    tool_version:
        Census version string (e.g. ``"1.2.0"``).
    suspect_dir:
        Absolute path to the suspect directory — used as the
        ``originalUriBaseIds`` anchor.
    """
    results: list[dict[str, Any]] = []

    for m in report.matches:
        att_id = m.attestation_id or "unknown"
        level = m.level or "unknown"
        short_hash = m.hash_hex[:16]

        uri = quote(PurePosixPath(m.suspect_path).as_posix(), safe="/")

        props: dict[str, Any] = {}
        if m.attestation_id is not None:
            props["attestation_id"] = m.attestation_id
        if m.level is not None:
            props["attestation_level"] = m.level
        if m.timestamp is not None:
            props["first_attested"] = m.timestamp
        if m.source is not None:
            props["source"] = m.source
        if m.inventory_path is not None:
            props["inventory_path"] = m.inventory_path

        result_obj: dict[str, Any] = {
            "ruleId": _RULE_ID,
            "level": "error",
            "message": {
                "text": (
                    f"File {m.suspect_path} matches attested hash "
                    f"{short_hash}... (attestation {att_id}, level {level})"
                ),
            },
            "locations": [
                {
                    "physicalLocation": {
                        "artifactLocation": {
                            "uri": uri,
                            "uriBaseId": "SUSPECT_DIR",
                        },
                    },
                },
            ],
            "fingerprints": {"sha256": m.hash_hex},
        }
        if props:
            result_obj["properties"] = props

        results.append(result_obj)

    dir_uri = _dir_to_file_uri(suspect_dir)

    return {
        "$schema": _SARIF_SCHEMA,
        "version": "2.1.0",
        "runs": [
            {
                "tool": {
                    "driver": {
                        "name": "certisigma-census",
                        "version": tool_version,
                        "informationUri": "https://certisigma.ch/census",
                        "rules": [copy.deepcopy(_RULE)],
                    },
                },
                "invocations": [{"executionSuccessful": True}],
                "results": results,
                "originalUriBaseIds": {
                    "SUSPECT_DIR": {"uri": dir_uri},
                },
            },
        ],
    }


def _dir_to_file_uri(path: str) -> str:
    """Convert an absolute directory path to a ``file:///`` URI with trailing slash.

    Path components are percent-encoded per RFC 3986 to handle spaces,
    ``#``, and other special characters safely.  Only POSIX paths are
    supported (Census is a Linux CLI tool).
    """
    posix = PurePosixPath(path).as_posix()
    if not posix.startswith("/"):
        posix = "/" + posix
    if not posix.endswith("/"):
        posix += "/"
    return "file://" + quote(posix, safe="/")
