import re
from typing import List

from brox.model import AssetChange, CapabilityChange, Risk


SENSITIVE_PATHS = [
    r"/etc/.*",
    r"/\.ssh/.*",
    r".*\.pem$",
    r"/var/run/.*",
    r"^/\*\*$",
]


def assess_risk(
    assets_added: List[AssetChange],
    assets_changed: List[AssetChange],
    capabilities_added: List[CapabilityChange],
    capabilities_widened: List[CapabilityChange],
) -> Risk:
    level = "low"
    reasons = []

    for cap in capabilities_added:
        if cap.capability == "exec.shell":
            level = upgrade_risk_level(level, "high")
            reasons.append("Shell execution capability added")

        if cap.capability == "db.write":
            level = upgrade_risk_level(level, "high")
            reasons.append("Database write capability added")

        if cap.capability == "filesystem.write":
            if is_sensitive_scope(cap.scope):
                level = upgrade_risk_level(level, "high")
                reasons.append(
                    f"Filesystem write to sensitive path added: {cap.scope}"
                )
            else:
                level = upgrade_risk_level(level, "medium")
                reasons.append(f"Filesystem write capability added: {cap.scope}")

        if cap.capability == "network.egress":
            level = upgrade_risk_level(level, "medium")
            reasons.append(f"New network egress to {cap.scope}")

    for cap in capabilities_widened:
        if cap.capability == "filesystem.read" or cap.capability == "filesystem.write":
            if is_sensitive_scope(cap.to_scope):
                level = upgrade_risk_level(level, "high")
                reasons.append(
                    f"{cap.capability} widened to sensitive path: {cap.from_scope} → {cap.to_scope}"
                )
            else:
                level = upgrade_risk_level(level, "medium")
                reasons.append(
                    f"{cap.capability} scope widened: {cap.from_scope} → {cap.to_scope}"
                )

    for asset in assets_changed:
        if asset.kind == "prompt":
            if "system" in asset.id.lower():
                level = upgrade_risk_level(level, "medium")
                reasons.append(f"System prompt modified: {asset.id}")

    for asset in assets_added:
        if asset.kind == "agent_framework":
            level = upgrade_risk_level(level, "medium")
            reasons.append(f"New agent framework added: {asset.id}")

        if asset.kind == "mcp_server":
            level = upgrade_risk_level(level, "medium")
            reasons.append(f"New MCP server added: {asset.id}")

    has_exec = False
    for cap in capabilities_added:
        if cap.capability == "exec.shell":
            has_exec = True
            break

    has_broad_fs_write = False
    for cap in capabilities_added:
        if cap.capability == "filesystem.write" and is_sensitive_scope(cap.scope):
            has_broad_fs_write = True
            break

    has_egress = False
    for cap in capabilities_added:
        if cap.capability == "network.egress":
            has_egress = True
            break

    if has_exec and has_broad_fs_write:
        level = "critical"
        reasons.append(
            "CRITICAL: Shell execution combined with broad filesystem write access"
        )

    if has_broad_fs_write and has_egress:
        level = upgrade_risk_level(level, "critical")
        reasons.append(
            "CRITICAL: Sensitive filesystem access combined with network egress"
        )

    reasons = list(dict.fromkeys(reasons))

    return Risk(level=level, reasons=reasons)


def upgrade_risk_level(current: str, new: str) -> str:
    levels = ["low", "medium", "high", "critical"]
    if current in levels:
        current_idx = levels.index(current)
    else:
        current_idx = 0
    
    if new in levels:
        new_idx = levels.index(new)
    else:
        new_idx = 0
    return levels[max(current_idx, new_idx)]


def is_sensitive_scope(scope: str) -> bool:
    for pattern in SENSITIVE_PATHS:
        if re.match(pattern, scope):
            return True
    return False
