"""
SARIF 2.1.0 output for PQC Side-Channel Scanner.

SARIF (Static Analysis Results Interchange Format) is the standard
format used by GitHub Advanced Security, Microsoft Defender, and
enterprise security tools. Adding this output makes pqc-scanner
pluggable into any enterprise security pipeline without custom
integration work.

Usage:
    python cli.py scan --algorithm both --format sarif --output results
    python cli.py liboqs --format sarif --output results

GitHub Actions integration:
    - run: python cli.py liboqs --traces 50000 --format sarif --output pqc
    - uses: github/codeql-action/upload-sarif@v3
      with:
        sarif_file: pqc.sarif
"""

import json
from datetime import datetime, timezone
from typing import List, Dict, Any


TOOL_NAME    = "pqc-scanner"
TOOL_VERSION = "0.3.0"
TOOL_URI     = "https://github.com/Disha23112004/pqc-scanner"

# SARIF severity mapping from our severity levels
SARIF_LEVEL = {
    "CRITICAL": "error",
    "HIGH":     "warning",
    "MEDIUM":   "note",
    "PASS":     "none",
}

# Rule definitions for each operation we scan
# These are static -- one rule per operation type
RULES = [
    {
        "id":   "PQC001",
        "name": "TimingLeakDecapsulation",
        "shortDescription": {
            "text": "ML-KEM Decapsulation timing leak detected"
        },
        "fullDescription": {
            "text": (
                "The ML-KEM decapsulation operation exhibits statistically significant "
                "timing variation between fixed and random inputs (|t| > 4.5, TVLA methodology). "
                "This indicates the implementation is not constant-time and may leak "
                "information about the secret key through timing side-channels."
            )
        },
        "helpUri": "https://github.com/Disha23112004/pqc-scanner#methodology",
        "properties": {
            "tags": ["cryptography", "pqc", "ml-kem", "side-channel", "timing"]
        }
    },
    {
        "id":   "PQC002",
        "name": "TimingLeakEncapsulation",
        "shortDescription": {
            "text": "ML-KEM Encapsulation timing leak detected"
        },
        "fullDescription": {
            "text": (
                "The ML-KEM encapsulation operation exhibits statistically significant "
                "timing variation between fixed and random inputs (|t| > 4.5, TVLA methodology). "
                "This indicates the implementation is not constant-time."
            )
        },
        "helpUri": "https://github.com/Disha23112004/pqc-scanner#methodology",
        "properties": {
            "tags": ["cryptography", "pqc", "ml-kem", "side-channel", "timing"]
        }
    },
    {
        "id":   "PQC003",
        "name": "TimingLeakKeyGen",
        "shortDescription": {
            "text": "Key generation timing leak detected"
        },
        "fullDescription": {
            "text": (
                "The key generation operation exhibits statistically significant "
                "timing variation (|t| > 4.5, TVLA methodology)."
            )
        },
        "helpUri": "https://github.com/Disha23112004/pqc-scanner#methodology",
        "properties": {
            "tags": ["cryptography", "pqc", "side-channel", "timing"]
        }
    },
    {
        "id":   "PQC004",
        "name": "TimingLeakSign",
        "shortDescription": {
            "text": "ML-DSA Sign timing leak detected"
        },
        "fullDescription": {
            "text": (
                "The ML-DSA signing operation exhibits statistically significant "
                "timing variation (|t| > 4.5, TVLA methodology)."
            )
        },
        "helpUri": "https://github.com/Disha23112004/pqc-scanner#methodology",
        "properties": {
            "tags": ["cryptography", "pqc", "ml-dsa", "side-channel", "timing"]
        }
    },
    {
        "id":   "PQC005",
        "name": "TimingLeakVerify",
        "shortDescription": {
            "text": "ML-DSA Verify timing leak detected"
        },
        "fullDescription": {
            "text": (
                "The ML-DSA verification operation exhibits statistically significant "
                "timing variation (|t| > 4.5, TVLA methodology)."
            )
        },
        "helpUri": "https://github.com/Disha23112004/pqc-scanner#methodology",
        "properties": {
            "tags": ["cryptography", "pqc", "ml-dsa", "side-channel", "timing"]
        }
    },
    {
        "id":   "PQC006",
        "name": "TimingLeakGeneric",
        "shortDescription": {
            "text": "PQC operation timing leak detected"
        },
        "fullDescription": {
            "text": (
                "A PQC cryptographic operation exhibits statistically significant "
                "timing variation (|t| > 4.5, TVLA methodology)."
            )
        },
        "helpUri": "https://github.com/Disha23112004/pqc-scanner#methodology",
        "properties": {
            "tags": ["cryptography", "pqc", "side-channel", "timing"]
        }
    },
]

# Map operation names to rule IDs
OPERATION_RULE_MAP = {
    "ML-KEM Decapsulation":  "PQC001",
    "KEM Decapsulation":     "PQC001",
    "Decapsulation":         "PQC001",
    "ML-KEM Encapsulation":  "PQC002",
    "KEM Encapsulation":     "PQC002",
    "Encapsulation":         "PQC002",
    "KEM KeyGen":            "PQC003",
    "KeyGen":                "PQC003",
    "DSA Sign":              "PQC004",
    "Sign":                  "PQC004",
    "DSA Verify":            "PQC005",
    "Verify":                "PQC005",
}


def _get_rule_id(operation: str) -> str:
    """Map operation name to SARIF rule ID."""
    return OPERATION_RULE_MAP.get(operation, "PQC006")


def _get_rule_index(rule_id: str) -> int:
    """Get rule index in RULES list by rule ID."""
    for i, rule in enumerate(RULES):
        if rule["id"] == rule_id:
            return i
    return 5  # PQC006 generic


def build_sarif(findings: List[Dict[str, Any]], target: str) -> Dict[str, Any]:
    """
    Build a SARIF 2.1.0 document from scanner findings.

    Args:
        findings: list of finding dicts from the scanner
        target: target label string (e.g. "liboqs ML-KEM-768")

    Returns:
        SARIF document as a Python dict (serialize with json.dumps)
    """
    results = []

    for finding in findings:
        if not finding.get("leaking", False):
            continue  # Only include actual findings in SARIF

        operation = finding.get("operation", "Unknown")
        rule_id   = _get_rule_id(operation)
        rule_idx  = _get_rule_index(rule_id)
        severity  = finding.get("severity", "HIGH")
        t_stat    = finding.get("t_statistic", 0)
        delta_ns  = finding.get("difference_ns", 0)
        p_value   = finding.get("p_value", 0)
        level     = SARIF_LEVEL.get(severity, "warning")

        message = (
            f"{operation} timing leak confirmed. "
            f"t={t_stat:.4f} (threshold: |t|>4.5), "
            f"delta={delta_ns:.1f}ns, "
            f"p={p_value:.2e}. "
            f"The implementation execution time depends on input values, "
            f"which may allow an attacker to recover secret key material "
            f"through repeated timing measurements."
        )

        result = {
            "ruleId":    rule_id,
            "ruleIndex": rule_idx,
            "level":     level,
            "message": {
                "text": message
            },
            "locations": [
                {
                    "physicalLocation": {
                        "artifactLocation": {
                            "uri":       "harness/ml_kem_harness.c",
                            "uriBaseId": "%SRCROOT%"
                        },
                        "region": {
                            "startLine": 1
                        }
                    },
                    "logicalLocations": [
                        {
                            "name":            operation,
                            "fullyQualifiedName": f"{target}/{operation}",
                            "kind":            "function"
                        }
                    ]
                }
            ],
            "properties": {
                "t_statistic": t_stat,
                "delta_ns":    delta_ns,
                "p_value":     p_value,
                "severity":    severity,
                "target":      target,
                "traces":      finding.get("num_traces", 0),
                "methodology": "TVLA / Welch t-test / ISO 17825"
            }
        }
        results.append(result)

    sarif_doc = {
        "version": "2.1.0",
        "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
        "runs": [
            {
                "tool": {
                    "driver": {
                        "name":            TOOL_NAME,
                        "version":         TOOL_VERSION,
                        "informationUri":  TOOL_URI,
                        "shortDescription": {
                            "text": "Automated timing side-channel scanner for NIST PQC standards"
                        },
                        "rules": RULES
                    }
                },
                "invocations": [
                    {
                        "executionSuccessful": True,
                        "commandLine": f"python cli.py liboqs",
                        "startTimeUtc": datetime.now(timezone.utc).isoformat(),
                        "properties": {
                            "target":      target,
                            "methodology": "TVLA",
                            "threshold":   "|t| > 4.5"
                        }
                    }
                ],
                "results": results,
                "properties": {
                    "target":    target,
                    "scanner":   f"{TOOL_NAME} v{TOOL_VERSION}",
                    "timestamp": datetime.now(timezone.utc).isoformat()
                }
            }
        ]
    }

    return sarif_doc


def save_sarif(findings: List[Dict[str, Any]], target: str, output_path: str) -> str:
    """
    Build and save SARIF document to file.

    Args:
        findings: list of finding dicts
        target: target label
        output_path: path without extension (e.g. "results" → "results.sarif")

    Returns:
        path to saved SARIF file
    """
    sarif_path = f"{output_path}.sarif"
    sarif_doc  = build_sarif(findings, target)

    with open(sarif_path, "w", encoding="utf-8") as f:
        json.dump(sarif_doc, f, indent=2)

    return sarif_path
