"""Project type detection for slop-mop CLI."""

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

from slopmop.checks.base import find_tool

# Tools required by specific checks: (tool_name, check_name, install_command)
# Used during `sm init` to auto-disable checks whose tools aren't available.
# find_tool() resolves these via project venv → .venv → VIRTUAL_ENV → PATH.
# When sm is installed via pipx, most tools are bundled and found via PATH.
REQUIRED_TOOLS: List[Tuple[str, str, str]] = [
    # Lint & format (py-lint gate)
    ("black", "laziness:py-lint", "pip install black  # in your venv"),
    ("isort", "laziness:py-lint", "pip install isort  # in your venv"),
    ("autoflake", "laziness:py-lint", "pip install autoflake  # in your venv"),
    ("flake8", "laziness:py-lint", "pip install flake8  # in your venv"),
    # Static analysis & types
    ("vulture", "laziness:dead-code", "pip install vulture  # in your venv"),
    ("pyright", "overconfidence:py-types", "pip install pyright  # in your venv"),
    # Security scanning
    ("bandit", "myopia:security-scan", "pip install bandit  # in your venv"),
    ("semgrep", "myopia:security-scan", "pip install semgrep  # in your venv"),
    (
        "detect-secrets",
        "myopia:security-scan",
        "pip install detect-secrets  # in your venv",
    ),
    ("pip-audit", "myopia:security-audit", "pip install pip-audit  # in your venv"),
    # Complexity scanning (not bundled — install system-wide or in venv)
    (
        "radon",
        "laziness:complexity",
        "pip install radon  # in your venv or: brew install radon",
    ),
]


def _detect_tools(project_root: Path) -> Dict[str, Any]:
    """Detect which required tools are available.

    Uses find_tool() from base.py which handles venv/bin, .venv/bin,
    Windows Scripts paths, and falls back to shutil.which().

    Returns:
        Dict with:
        - available_tools: list of tool names that are installed
        - missing_tools: list of (tool_name, check_name, install_command) for missing tools
    """
    available: List[str] = []
    missing: List[Tuple[str, str, str]] = []

    for tool_name, check_name, install_cmd in REQUIRED_TOOLS:
        found = find_tool(tool_name, str(project_root))
        if found:
            available.append(tool_name)
        else:
            missing.append((tool_name, check_name, install_cmd))

    return {
        "available_tools": available,
        "missing_tools": missing,
    }


def _detect_python(project_root: Path) -> bool:
    """Check for Python project indicators."""
    py_indicators = ["setup.py", "pyproject.toml", "requirements.txt", "Pipfile"]
    for indicator in py_indicators:
        if (project_root / indicator).exists():
            return True
    return any(project_root.glob("**/*.py"))


def _detect_javascript(project_root: Path) -> bool:
    """Check for JavaScript project indicators."""
    js_indicators = ["package.json", "tsconfig.json"]
    for indicator in js_indicators:
        if (project_root / indicator).exists():
            return True
    return any(project_root.glob("**/*.js")) or any(project_root.glob("**/*.ts"))


def _detect_typescript(project_root: Path) -> bool:
    """Check specifically for TypeScript."""
    ts_indicators = ["tsconfig.json", "tsconfig.ci.json"]
    for indicator in ts_indicators:
        if (project_root / indicator).exists():
            return True
    return any(project_root.glob("**/*.ts"))


def _detect_test_dirs(project_root: Path) -> list[str]:
    """Find test directories."""
    test_dirs: list[str] = []
    for test_dir in ["tests", "test", "spec", "__tests__"]:
        test_path = project_root / test_dir
        if test_path.is_dir():
            test_dirs.append(str(test_path.relative_to(project_root)))
    return test_dirs


def _detect_pytest(project_root: Path) -> bool:
    """Check for pytest configuration."""
    pyproject = project_root / "pyproject.toml"
    if pyproject.exists() and "pytest" in pyproject.read_text():
        return True

    setup_cfg = project_root / "setup.cfg"
    if setup_cfg.exists() and "pytest" in setup_cfg.read_text():
        return True

    return (project_root / "pytest.ini").exists() or (
        project_root / "conftest.py"
    ).exists()


def _detect_jest(project_root: Path) -> bool:
    """Check for Jest configuration."""
    package_json = project_root / "package.json"
    if not package_json.exists():
        return False

    try:
        pkg = json.loads(package_json.read_text())
        if "jest" in pkg.get("devDependencies", {}):
            return True
        if "jest" in pkg.get("dependencies", {}):
            return True
        if "test" in pkg.get("scripts", {}):
            if "jest" in pkg["scripts"]["test"]:
                return True
    except json.JSONDecodeError:
        pass
    return False


def _recommend_gates(detected: Dict[str, Any]) -> list[str]:
    """Determine recommended gates based on detection."""
    recommended: list[str] = []
    if detected["has_python"]:
        recommended.extend(
            [
                "laziness:py-lint",
                "overconfidence:py-tests",
                "overconfidence:py-static-analysis",
            ]
        )
        if detected["has_pytest"]:
            recommended.append("deceptiveness:py-coverage")

    if detected["has_javascript"]:
        recommended.extend(["laziness:js-lint", "overconfidence:js-tests"])
        if detected["has_jest"]:
            recommended.append("deceptiveness:js-coverage")
        if detected["has_typescript"]:
            recommended.append("overconfidence:js-types")

    return recommended


def _recommend_profile(detected: Dict[str, Any]) -> str:
    """Determine recommended profile based on detection."""
    if detected["has_python"] and detected["has_javascript"]:
        return "pr"
    elif detected["has_python"]:
        return "python"
    elif detected["has_javascript"]:
        return "javascript"
    return "commit"


def detect_project_type(project_root: Path) -> Dict[str, Any]:
    """Auto-detect project type and characteristics.

    Returns a dict with detected features:
    - has_python: bool
    - has_javascript: bool
    - has_typescript: bool
    - has_tests_dir: bool
    - has_pytest: bool
    - has_jest: bool
    - test_dirs: list of test directory paths
    - recommended_profile: str
    - recommended_gates: list of str
    - available_tools: list of str
    - missing_tools: list of (tool_name, check_name, install_command)
    """
    detected: Dict[str, Any] = {
        "has_python": _detect_python(project_root),
        "has_javascript": _detect_javascript(project_root),
        "has_typescript": _detect_typescript(project_root),
        "has_pytest": _detect_pytest(project_root),
        "has_jest": _detect_jest(project_root),
        "test_dirs": _detect_test_dirs(project_root),
    }

    detected["has_tests_dir"] = bool(detected["test_dirs"])
    detected["recommended_gates"] = _recommend_gates(detected)
    detected["recommended_profile"] = _recommend_profile(detected)

    # Detect tool availability
    tool_info = _detect_tools(project_root)
    detected["available_tools"] = tool_info["available_tools"]
    detected["missing_tools"] = tool_info["missing_tools"]

    return detected
