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

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


HTTP_PATTERNS = [
    r"https?://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})",
    r"requests\.(get|post|put|delete|patch)\s*\(['\"]https?://([^'\"]+)",
    r"fetch\s*\(['\"]https?://([^'\"]+)",
    r"axios\.(get|post|put|delete|patch)\s*\(['\"]https?://([^'\"]+)",
    r"http\.get\(['\"]https?://([^'\"]+)",
    r"urllib\.request\.urlopen\(['\"]https?://([^'\"]+)",
]


INTERNAL_PATTERNS = [
    r"localhost",
    r"127\.0\.0\.1",
    r"0\.0\.0\.0",
    r".*\.local",
    r"example\.com",
    r"test\.com",
]


def detect_egress(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_domains = 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:
            egress_items = detect_egress_in_file(code_file, repo_path)
            for domain, evidence in egress_items:
                if domain not in detected_domains:
                    detected_domains.add(domain)
                    component, caps = create_egress_component(domain, evidence)
                    components.append(component)
                    capabilities.extend(caps)
        except Exception as e:
            print(f"Warning: Failed to detect egress in {code_file}: {e}")

    return components, capabilities


def detect_egress_in_file(code_file: Path, repo_root: Path) -> List[Tuple[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))

    domains = set()
    for pattern in HTTP_PATTERNS:
        matches = re.finditer(pattern, content, re.IGNORECASE)
        for match in matches:
            if match.lastindex:
                domain = match.group(match.lastindex)
            else:
                domain = match.group(1)

            domain = domain.split("/")[0].split(":")[0]
            domains.add(domain)

    filtered_domains = []
    for domain in domains:
        is_internal = False
        for pattern in INTERNAL_PATTERNS:
            if re.match(pattern, domain):
                is_internal = True
                break

        if not is_internal:
            filtered_domains.append(domain)

    for domain in filtered_domains:
        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((domain, evidence))

    return detected


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

    properties = [
        Property(name="brox.ai.asset.kind", value="egress_sink"),
        Property(name="brox.egress.domain", value=domain),
        Property(name="brox.location.file", value=evidence.split(":")[0]),
    ]

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

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

    return component, [capability]
