import re
from pathlib import Path
from typing import List, Tuple

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


LLM_PROVIDERS = {
    "openai": {
        "domains": ["api.openai.com", "openai.azure.com"],
        "import_patterns": [r"import\s+openai", r"from\s+openai\s+import", r"OpenAI\("],
        "package_names": ["openai"],
    },
    "anthropic": {
        "domains": ["api.anthropic.com"],
        "import_patterns": [
            r"import\s+anthropic",
            r"from\s+anthropic\s+import",
            r"Anthropic\(",
        ],
        "package_names": ["anthropic"],
    },
    "bedrock": {
        "domains": ["bedrock-runtime.*.amazonaws.com"],
        "import_patterns": [r"boto3.*bedrock", r"bedrock-runtime"],
        "package_names": ["boto3"],
    },
    "azure": {
        "domains": ["openai.azure.com", "*.openai.azure.com"],
        "import_patterns": [r"AzureOpenAI\(", r"azure.*openai"],
        "package_names": [],
    },
    "cohere": {
        "domains": ["api.cohere.ai"],
        "import_patterns": [r"import\s+cohere", r"from\s+cohere\s+import"],
        "package_names": ["cohere"],
    },
}


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

    code_patterns = ["*.py", "*.js", "*.ts", "*.jsx", "*.tsx", "*.go", "*.java"]
    code_files = []
    for pattern in code_patterns:
        code_files.extend(repo_path.rglob(pattern))

    detected = set()

    for code_file in code_files:
        if any(
            part in code_file.parts
            for part in ["node_modules", ".git", "venv", "__pycache__", "dist", "build"]
        ):
            continue

        try:
            providers_in_file = detect_providers_in_file(code_file, repo_path)
            for provider, domain, evidence in providers_in_file:
                key = (provider, domain)
                if key not in detected:
                    detected.add(key)
                    component, caps = create_provider_component(provider, domain, evidence)
                    components.append(component)
                    capabilities.extend(caps)
        except Exception as e:
            print(f"Warning: Failed to detect providers in {code_file}: {e}")

    return components, capabilities


def detect_providers_in_file(
    code_file: Path, repo_root: Path
) -> List[Tuple[str, str, str]]:
    detected = []

    try:
        with open(code_file, encoding="utf-8", errors="ignore") as f:
            content = f.read()
    except Exception:
        return detected

    rel_path = str(code_file.relative_to(repo_root))

    for provider, config in LLM_PROVIDERS.items():
        for pattern in config["import_patterns"]:
            matches = list(re.finditer(pattern, content))
            if matches:
                line_num = content[: matches[0].start()].count("\n") + 1
                evidence = f"{rel_path}:{line_num}"

                domain = None
                for dom in config["domains"]:
                    if dom in content:
                        domain = dom
                        break

                if not domain:
                    if config["domains"]:
                        domain = config["domains"][0]
                    else:
                        domain = "unknown"

                detected.append((provider, domain, evidence))
                break

        for domain in config["domains"]:
            if "*" not in domain and domain in content:
                match = re.search(re.escape(domain), content)
                if match:
                    line_num = content[: match.start()].count("\n") + 1
                    evidence = f"{rel_path}:{line_num}"
                    detected.append((provider, domain, evidence))
                    break

    return detected


def create_provider_component(
    provider: str, domain: str, evidence: str
) -> Tuple[Component, List[Capability]]:
    bom_ref = generate_bom_ref("llm_provider", provider, domain)

    properties = [
        Property(name="brox.ai.asset.kind", value="llm_provider"),
        Property(name="brox.llm.provider", value=provider),
        Property(name="brox.llm.endpoint", value=domain),
        Property(name="brox.location.file", value=evidence.split(":")[0]),
    ]

    component = Component(
        type="service",
        name=f"llm-provider:{provider}:{domain}",
        version="1",
        bom_ref=bom_ref,
        properties=properties,
    )

    capability = Capability(
        name="network.egress", scope=domain, asset=bom_ref, evidence=[evidence]
    )

    return component, [capability]
