#!/usr/bin/env python3

from __future__ import annotations

import re
import sys
import tomllib
from pathlib import Path
from typing import Any


def extract_workspace_source_roots(flake_path: Path) -> dict[str, list[str]]:
    source = flake_path.read_text()
    match = re.search(r"workspaceSourceRoots\s*=\s*\{(.*?)\n\s*\};", source, re.S)
    if match is None:
        raise ValueError(f"Could not find workspaceSourceRoots in {flake_path}")

    roots: dict[str, list[str]] = {}
    for name, body in re.findall(r"\n\s*(\w+)\s*=\s*\[(.*?)\];", match.group(1), re.S):
        roots[name] = re.findall(r'"([^"]+)"', body)
    return roots


def collect_path_dependencies(value: Any) -> list[str]:
    found: list[str] = []

    if isinstance(value, dict):
        path = value.get("path")
        if isinstance(path, str):
            found.append(path)
        for nested in value.values():
            found.extend(collect_path_dependencies(nested))
    elif isinstance(value, list):
        for nested in value:
            found.extend(collect_path_dependencies(nested))

    return found


def workspace_manifests(repo_root: Path, workspace_name: str) -> list[Path]:
    workspace_manifest = repo_root / workspace_name / "Cargo.toml"
    manifests = [workspace_manifest]
    workspace_data = tomllib.loads(workspace_manifest.read_text())
    members = workspace_data.get("workspace", {}).get("members", [])

    for member in members:
        for candidate in (workspace_manifest.parent).glob(member):
            manifest = candidate if candidate.name == "Cargo.toml" else candidate / "Cargo.toml"
            if manifest.is_file():
                manifests.append(manifest)

    unique_manifests: list[Path] = []
    seen: set[Path] = set()
    for manifest in manifests:
        resolved = manifest.resolve()
        if resolved in seen:
            continue
        seen.add(resolved)
        unique_manifests.append(manifest)
    return unique_manifests


def required_root(dep_rel: Path) -> str:
    parts = dep_rel.parts
    if not parts:
        return ""
    if parts[0] == "crates" and len(parts) >= 2:
        return "/".join(parts[:2])
    return parts[0]


def is_covered(dep_rel: str, configured_roots: list[str]) -> bool:
    return any(dep_rel == root or dep_rel.startswith(f"{root}/") for root in configured_roots)


def main() -> int:
    repo_root = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path.cwd().resolve()
    flake_path = repo_root / "flake.nix"
    workspace_roots = extract_workspace_source_roots(flake_path)

    failures: list[str] = []

    for workspace_name, configured_roots in sorted(workspace_roots.items()):
        workspace_manifest = repo_root / workspace_name / "Cargo.toml"
        if not workspace_manifest.is_file():
            continue

        for manifest in workspace_manifests(repo_root, workspace_name):
            manifest_data = tomllib.loads(manifest.read_text())
            for dep_path in collect_path_dependencies(manifest_data):
                dependency_dir = (manifest.parent / dep_path).resolve()
                try:
                    dep_rel = dependency_dir.relative_to(repo_root)
                except ValueError:
                    continue

                dep_rel_str = dep_rel.as_posix()
                if is_covered(dep_rel_str, configured_roots):
                    continue

                needed = required_root(dep_rel)
                manifest_rel = manifest.relative_to(repo_root).as_posix()
                failures.append(
                    f"{workspace_name}: missing source root '{needed}' for dependency "
                    f"'{dep_rel_str}' referenced by {manifest_rel}"
                )

    if failures:
        print("workspaceSourceRoots is missing path dependencies:", file=sys.stderr)
        for failure in failures:
            print(f"  - {failure}", file=sys.stderr)
        return 1

    print("workspaceSourceRoots covers all workspace path dependencies.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
