#!/usr/bin/env python3
"""Generate review-first Cloudflare and Codacy profiles. Dry-run by default."""

from __future__ import annotations
import argparse
from pathlib import Path
import subprocess
import sys

AVAILABLE = "AVAILABLE"
NA = "NOT_APPLICABLE_TO_CAPABILITY"


def has(root: Path, *names: str) -> bool:
    return any((root / name).exists() for name in names)


def stacks(root: Path) -> list[str]:
    found: list[str] = []
    checks = [
        ("javascript-typescript", has(root, "package.json", "pnpm-lock.yaml", "yarn.lock")),
        ("web", has(root, "next.config.js", "next.config.mjs", "next.config.ts", "vite.config.js", "vite.config.ts", "astro.config.mjs")),
        ("cloudflare-native", has(root, "wrangler.toml", "wrangler.json", "wrangler.jsonc")),
        ("java-jvm", has(root, "pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts")),
        ("android", any(root.glob("**/AndroidManifest.xml"))),
        ("dotnet", any(root.glob("**/*.csproj")) or any(root.glob("*.sln"))),
        ("unity", (root / "Assets").is_dir() and (root / "ProjectSettings").is_dir()),
        ("rust", has(root, "Cargo.toml")),
        ("python", has(root, "pyproject.toml", "requirements.txt", "Pipfile")),
        ("php", has(root, "composer.json")),
        ("infrastructure", any(root.glob("**/*.tf"))),
        ("container", has(root, "Dockerfile", "compose.yaml", "compose.yml", "docker-compose.yml")),
    ]
    for name, matched in checks:
        if matched:
            found.append(name)
    return found or ["generic"]


def repo_name(root: Path) -> str:
    try:
        value = subprocess.run(
            ["git", "-C", str(root), "config", "--get", "remote.origin.url"],
            check=True, capture_output=True, text=True, timeout=5,
        ).stdout.strip().rstrip("/").removesuffix(".git")
        if value.startswith("git@"):
            return value.split(":", 1)[1]
        if "://" in value:
            parts = value.rsplit("/", 2)
            return f"{parts[-2]}/{parts[-1]}"
    except (OSError, subprocess.SubprocessError, IndexError):
        pass
    return root.name


def listed(items: list[str], indent: int = 4) -> str:
    values = sorted(set(items))
    return "\n".join(" " * indent + "- " + item for item in values) if values else " " * indent + "[]"


def cf_profile(repo: str, detected: list[str]) -> str:
    selected = ["dns", "observability"]
    rejected: list[str] = []
    current = set(detected)
    if "cloudflare-native" in current:
        selected += ["workers", "bindings", "preview-deployments"]
    elif current & {"web", "javascript-typescript"}:
        selected += ["pages-or-workers", "preview-deployments", "waf", "turnstile"]
    elif current & {"java-jvm", "dotnet", "python", "php", "rust", "container"}:
        selected += ["edge-proxy-or-tunnel", "waf", "access"]
        rejected += ["forced-workers-runtime-migration"]
    else:
        selected += ["documentation-hosting"]
    if current & {"android", "dotnet", "unity", "java-jvm", "rust"}:
        selected += ["r2-artifact-distribution", "workers-update-metadata", "access-internal-artifacts"]
    if "infrastructure" in current:
        selected += ["terraform-or-pulumi", "zero-trust", "tunnel", "drift-detection"]
    return f"""schema_version: 1
repository:
  name: {repo}
  stacks:
{listed(detected, 4)}
providers:
  cloudflare:
    required_readiness: true
    status: {AVAILABLE}
    selected_capabilities:
{listed(selected)}
    rejected_capabilities:
{listed(rejected)}
    auth_mode_interactive: oauth
    auth_mode_ci: scoped-api-token
    required_secrets: []
    read_toolsets:
      - cloudflare-docs-read
      - cloudflare-builds-read
      - cloudflare-observability-read
    write_toolsets: []
"""


def codacy_profile(repo: str, detected: list[str]) -> str:
    selected = ["cloud-analysis", "cloud-cli", "coverage", "mcp-read"]
    rejected: list[str] = []
    tools = ["semgrep"]
    current = set(detected)
    guardrails = AVAILABLE if current & {"javascript-typescript", "web", "python", "java-jvm"} else NA
    if guardrails == AVAILABLE:
        selected += ["guardrails"]
    else:
        rejected += ["guardrails-for-unsupported-stack"]
    if "java-jvm" in current:
        tools += ["spotbugs"]
    if "unity" in current:
        tools += ["unity-roslyn-analyzers"]
    if "android" in current:
        tools += ["gradle-lint", "dependency-audit"]
    if "dotnet" in current:
        tools += ["dotnet-analyzers", "dependency-audit"]
    if "rust" in current:
        tools += ["cargo-clippy", "cargo-audit"]
    return f"""schema_version: 1
repository:
  name: {repo}
  stacks:
{listed(detected, 4)}
providers:
  codacy:
    required_readiness: true
    status: {AVAILABLE}
    selected_capabilities:
{listed(selected)}
    rejected_capabilities:
{listed(rejected)}
    analysis_mode: pilot
    guardrails_mode: {guardrails}
    client_side_tools:
{listed(tools)}
    coverage_strategy: native-tests-then-upload
    required_secrets: []
    read_toolsets:
      - codacy-repository-read
      - codacy-issues-read
      - codacy-security-read
      - codacy-coverage-read
    write_toolsets: []
"""


def doc(provider: str, profile: str) -> str:
    return f"""# {provider} integration

Generated readiness placeholder. Review `{profile}` before activation.

- Verify current official documentation and stack support.
- Select minimal capabilities and permissions.
- Store secret values outside the repository.
- Test read-only access before preview or production writes.
- Record plan, cost, activation, and rollback constraints.

This file authorizes no external write.
"""


def outputs(root: Path) -> dict[Path, str]:
    detected, repo = stacks(root), repo_name(root)
    return {
        root / ".agentic/providers/cloudflare.yaml": cf_profile(repo, detected),
        root / ".agentic/providers/codacy.yaml": codacy_profile(repo, detected),
        root / "docs/cloudflare-integration.md": doc("Cloudflare", ".agentic/providers/cloudflare.yaml"),
        root / "docs/codacy-integration.md": doc("Codacy", ".agentic/providers/codacy.yaml"),
    }


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--repo-root", type=Path, default=Path.cwd())
    parser.add_argument("--write", action="store_true")
    parser.add_argument("--force", action="store_true")
    args = parser.parse_args(argv)
    root = args.repo_root.resolve()
    if not root.is_dir():
        print(f"error: not a directory: {root}", file=sys.stderr)
        return 2
    planned = outputs(root)
    if not args.write:
        print("Dry run. No files were written.")
        for path in planned:
            print(path.relative_to(root))
        return 0
    for path, content in planned.items():
        if path.exists() and not args.force:
            print(f"error: {path} exists; pass --force", file=sys.stderr)
            return 3
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(content, encoding="utf-8")
        print(f"wrote {path.relative_to(root)}")
    return 0


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