import json
from pathlib import Path
from typing import Any, Dict, List, Tuple

import yaml

from brox.model import Capability, Component, Property, generate_bom_ref, hash_content


def detect_mcp(repo_path: Path) -> Tuple[List[Component], List[Capability]]:
    components = []
    capabilities = []

    config_files = []

    for pattern in ["mcp.json", "mcp.yaml", "mcp.yml", "servers.json"]:
        config_files.extend(repo_path.rglob(pattern))

    mcp_dir = repo_path / ".mcp"
    if mcp_dir.exists():
        config_files.extend(mcp_dir.rglob("*.json"))
        config_files.extend(mcp_dir.rglob("*.yaml"))
        config_files.extend(mcp_dir.rglob("*.yml"))

    for config_file in config_files:
        try:
            mcp_components, mcp_capabilities = parse_mcp_config(config_file, repo_path)
            components.extend(mcp_components)
            capabilities.extend(mcp_capabilities)
        except Exception as e:
            print(f"Warning: Failed to parse {config_file}: {e}")

    return components, capabilities


def parse_mcp_config(
    config_path: Path, repo_root: Path
) -> Tuple[List[Component], List[Capability]]:
    components = []
    capabilities = []

    with open(config_path) as f:
        if config_path.suffix in [".yaml", ".yml"]:
            config = yaml.safe_load(f)
        else:
            config = json.load(f)

    if not config:
        return components, capabilities

    rel_path = config_path.relative_to(repo_root)
    evidence_prefix = str(rel_path)

    with open(config_path) as f:
        content_hash = hash_content(f.read())

    servers = config.get("servers", config.get("mcpServers", {}))

    if isinstance(servers, dict):
        for server_name, server_config in servers.items():
            server_component, server_capabilities = parse_mcp_server(
                server_name, server_config, evidence_prefix, content_hash
            )
            if server_component:
                components.append(server_component)
                capabilities.extend(server_capabilities)

    if "tools" in config:
        server_name = config.get("name", config_path.stem)
        server_component, server_capabilities = parse_mcp_server(
            server_name, config, evidence_prefix, content_hash
        )
        if server_component:
            components.append(server_component)
            capabilities.extend(server_capabilities)

    return components, capabilities


def parse_mcp_server(
    server_name: str, server_config: Dict[str, Any], evidence_prefix: str, content_hash: str
) -> Tuple[Component, List[Capability]]:
    capabilities = []

    bom_ref = generate_bom_ref("mcp_server", server_name)
    properties = [
        Property(name="brox.ai.asset.kind", value="mcp_server"),
        Property(name="brox.mcp.server.name", value=server_name),
        Property(name="brox.location.file", value=evidence_prefix),
        Property(name="brox.hash.content", value=f"sha256:{content_hash}"),
    ]

    command = server_config.get("command", "")
    url = server_config.get("url", "")
    if url:
        properties.append(Property(name="brox.mcp.transport", value="http"))
    elif command:
        properties.append(Property(name="brox.mcp.transport", value="stdio"))

    component = Component(
        type="service", name=f"mcp-server:{server_name}", version="1", bom_ref=bom_ref, properties=properties
    )

    if is_filesystem_server(server_name, server_config):
        fs_caps = extract_filesystem_capabilities(
            server_name, server_config, bom_ref, evidence_prefix
        )
        capabilities.extend(fs_caps)

    if has_shell_exec(server_name, server_config):
        capabilities.append(
            Capability(
                name="exec.shell",
                scope="*",
                asset=bom_ref,
                evidence=[f"{evidence_prefix}:1"],
            )
        )

    if has_database_tools(server_name, server_config):
        tools = server_config.get("tools", [])
        for tool in tools:
            if isinstance(tool, str):
                tool_name = tool
            else:
                tool_name = tool.get("name", "")

            tool_lower = tool_name.lower()

            if "read" in tool_lower or "query" in tool_lower:
                capabilities.append(
                    Capability(
                        name="db.read",
                        scope="*",
                        asset=bom_ref,
                        evidence=[f"{evidence_prefix}:1"],
                    )
                )

            is_write = False
            if "write" in tool_lower:
                is_write = True
            if "insert" in tool_lower:
                is_write = True
            if "update" in tool_lower:
                is_write = True

            if is_write:
                capabilities.append(
                    Capability(
                        name="db.write",
                        scope="*",
                        asset=bom_ref,
                        evidence=[f"{evidence_prefix}:1"],
                    )
                )

    if has_network_tools(server_name, server_config):
        capabilities.append(
            Capability(
                name="network.egress",
                scope="*",
                asset=bom_ref,
                evidence=[f"{evidence_prefix}:1"],
            )
        )

    return component, capabilities


def is_filesystem_server(server_name: str, config: Dict[str, Any]) -> bool:
    name_lower = server_name.lower()

    fs_keywords = ["filesystem", "file", "fs", "directory", "storage"]
    for keyword in fs_keywords:
        if keyword in name_lower:
            return True

    command = config.get("command", "").lower()
    desc = config.get("description", "").lower()
    if "filesystem" in command or "filesystem" in desc:
        return True

    tools = config.get("tools", [])
    for tool in tools:
        if isinstance(tool, str):
            tool_name = tool
        else:
            tool_name = tool.get("name", "")

        tool_lower = tool_name.lower()
        tool_keywords = ["read_file", "write_file", "list_directory", "create_directory"]
        for keyword in tool_keywords:
            if keyword in tool_lower:
                return True

    return False


def extract_filesystem_capabilities(
    server_name: str, config: Dict[str, Any], bom_ref: str, evidence_prefix: str
) -> List[Capability]:
    capabilities = []

    allowed_roots = config.get("allowedRoots", config.get("roots", config.get("paths", [])))

    if allowed_roots:
        scopes = []
        for root in allowed_roots:
            if root.endswith("/**"):
                scopes.append(root)
            else:
                scopes.append(f"{root}/**")
    else:
        scopes = ["/**"]

    is_read_only = config.get("readOnly", False)

    has_read = True
    has_write = not is_read_only

    tools = config.get("tools", [])
    for tool in tools:
        if isinstance(tool, str):
            tool_name = tool
        else:
            tool_name = tool.get("name", "")

        tool_lower = tool_name.lower()

        if "read" in tool_lower or "list" in tool_lower or "get" in tool_lower:
            has_read = True

        write_keywords = ["write", "create", "delete", "update", "modify", "edit"]
        for keyword in write_keywords:
            if keyword in tool_lower:
                has_write = True
                break

    for scope in scopes:
        if has_read:
            capabilities.append(
                Capability(
                    name="filesystem.read",
                    scope=scope,
                    asset=bom_ref,
                    evidence=[f"{evidence_prefix}:1"],
                )
            )
        if has_write:
            capabilities.append(
                Capability(
                    name="filesystem.write",
                    scope=scope,
                    asset=bom_ref,
                    evidence=[f"{evidence_prefix}:1"],
                )
            )

    return capabilities


def has_shell_exec(server_name: str, config: Dict[str, Any]) -> bool:
    name_lower = server_name.lower()

    shell_keywords = ["shell", "exec", "command", "bash", "terminal"]
    for keyword in shell_keywords:
        if keyword in name_lower:
            return True

    tools = config.get("tools", [])
    for tool in tools:
        if isinstance(tool, str):
            tool_name = tool
        else:
            tool_name = tool.get("name", "")

        tool_lower = tool_name.lower()
        tool_keywords = ["exec", "shell", "command", "run", "bash"]
        for keyword in tool_keywords:
            if keyword in tool_lower:
                return True

    return False


def has_database_tools(server_name: str, config: Dict[str, Any]) -> bool:
    name_lower = server_name.lower()

    db_keywords = ["database", "db", "sql", "postgres", "mysql", "mongo", "redis"]
    for keyword in db_keywords:
        if keyword in name_lower:
            return True

    tools = config.get("tools", [])
    for tool in tools:
        if isinstance(tool, str):
            tool_name = tool
        else:
            tool_name = tool.get("name", "")

        tool_lower = tool_name.lower()
        tool_keywords = ["query", "sql", "db", "database"]
        for keyword in tool_keywords:
            if keyword in tool_lower:
                return True

    return False


def has_network_tools(server_name: str, config: Dict[str, Any]) -> bool:
    name_lower = server_name.lower()

    network_keywords = ["network", "http", "fetch", "request", "api", "web"]
    for keyword in network_keywords:
        if keyword in name_lower:
            return True

    tools = config.get("tools", [])
    for tool in tools:
        if isinstance(tool, str):
            tool_name = tool
        else:
            tool_name = tool.get("name", "")

        tool_lower = tool_name.lower()
        tool_keywords = ["fetch", "http", "request", "api", "web", "curl"]
        for keyword in tool_keywords:
            if keyword in tool_lower:
                return True

    return False
