"""
Project context detection, directory reading, and stdin helpers.
"""

import os
import select
import sys

from elgringo.cli.colors import Colors


# ── Constants ────────────────────────────────────────────────────────

_CODE_EXTENSIONS = {
    ".py", ".js", ".ts", ".tsx", ".jsx", ".go", ".rs", ".java", ".rb",
    ".vue", ".svelte", ".css", ".scss", ".html", ".json", ".yaml", ".yml",
    ".toml", ".sh", ".bash", ".sql", ".c", ".cpp", ".h", ".hpp",
    ".cs", ".swift", ".kt", ".scala", ".r", ".R", ".php", ".lua",
    ".ex", ".exs", ".erl", ".hs", ".ml", ".tf", ".dockerfile",
}

_SKIP_DIRS = {
    "node_modules", ".git", "__pycache__", ".venv", "venv", "dist", "build",
    ".tox", ".mypy_cache", ".pytest_cache", "egg-info", ".eggs",
    ".next", ".nuxt", "coverage", ".coverage", "htmlcov",
}


# ── Stdin ────────────────────────────────────────────────────────────

def read_stdin_if_piped() -> str:
    """Read piped stdin if available (non-blocking)."""
    if not sys.stdin.isatty():
        try:
            if select.select([sys.stdin], [], [], 0.1)[0]:
                return sys.stdin.read(50000)  # Cap at 50KB
        except Exception:
            pass
    return ""


# ── Directory Reading ────────────────────────────────────────────────

def read_directory_files(dir_path: str, max_bytes: int = 80000, max_files: int = 30) -> tuple:
    """Read code files from a directory up to a byte budget.

    Returns (combined_content: str, file_count: int, skipped_count: int).
    Files are sorted smallest-first so more files fit within the budget.
    When max_bytes is large (Gemini routing), individual file size limit scales up.
    """
    max_single_file = min(max_bytes // 2, 500000)  # single file can't be more than half the budget
    candidates = []
    for root, dirs, files in os.walk(dir_path):
        # Skip hidden and build directories
        dirs[:] = [d for d in dirs if d not in _SKIP_DIRS and not d.startswith('.')]
        for fname in files:
            ext = os.path.splitext(fname)[1].lower()
            if ext not in _CODE_EXTENSIONS:
                continue
            fpath = os.path.join(root, fname)
            try:
                size = os.path.getsize(fpath)
                if size > 0 and size <= max_single_file:
                    candidates.append((fpath, size))
            except OSError:
                pass

    # Sort smallest first to maximize file count within budget
    candidates.sort(key=lambda x: x[1])

    parts = []
    total_bytes = 0
    included = 0
    skipped = 0

    for fpath, size in candidates:
        if included >= max_files or total_bytes + size > max_bytes:
            skipped += 1
            continue
        try:
            with open(fpath, "r", errors="replace") as f:
                content = f.read()
            rel_path = os.path.relpath(fpath, dir_path)
            ext = os.path.splitext(fpath)[1].lstrip(".")
            parts.append(f"[FILE: {rel_path}]\n```{ext}\n{content}\n```")
            total_bytes += len(content)
            included += 1
        except Exception:
            skipped += 1

    skipped += len(candidates) - included - skipped
    combined = "\n\n".join(parts)
    return combined, included, max(0, skipped)


# ── Project Context ──────────────────────────────────────────────────

def gather_project_context(verbose: bool = False) -> str:
    """Auto-detect project context from the current directory."""
    import subprocess
    context_parts = []

    # Find git root
    try:
        git_root = subprocess.run(
            ["git", "rev-parse", "--show-toplevel"],
            capture_output=True, text=True, timeout=5,
        ).stdout.strip()
    except Exception:
        git_root = ""

    cwd = git_root or os.getcwd()

    # Project name from directory
    project_name = os.path.basename(cwd)
    context_parts.append(f"Project: {project_name}")
    context_parts.append(f"Directory: {cwd}")

    # Detect language/framework from key files
    markers = {
        "pyproject.toml": "Python",
        "package.json": "Node.js/JavaScript",
        "Cargo.toml": "Rust",
        "go.mod": "Go",
        "pom.xml": "Java (Maven)",
        "build.gradle": "Java (Gradle)",
        "Gemfile": "Ruby",
        "requirements.txt": "Python",
    }
    for filename, lang in markers.items():
        if os.path.exists(os.path.join(cwd, filename)):
            context_parts.append(f"Language: {lang}")
            break

    # Git branch + recent changes
    if git_root:
        try:
            branch = subprocess.run(
                ["git", "branch", "--show-current"],
                capture_output=True, text=True, timeout=5, cwd=cwd,
            ).stdout.strip()
            if branch:
                context_parts.append(f"Branch: {branch}")

            # Recent git status (modified files)
            status = subprocess.run(
                ["git", "status", "--porcelain"],
                capture_output=True, text=True, timeout=5, cwd=cwd,
            ).stdout.strip()
            if status:
                changed_files = [line[3:] for line in status.split("\n")[:10]]
                context_parts.append(f"Changed files: {', '.join(changed_files)}")

            # Last 3 commit messages
            log = subprocess.run(
                ["git", "log", "--oneline", "-3"],
                capture_output=True, text=True, timeout=5, cwd=cwd,
            ).stdout.strip()
            if log:
                context_parts.append(f"Recent commits:\n{log}")
        except Exception:
            pass

    # Read key config files (first 20 lines) for project understanding
    for config_file in ["pyproject.toml", "package.json", "README.md"]:
        filepath = os.path.join(cwd, config_file)
        if os.path.exists(filepath):
            try:
                with open(filepath, "r") as f:
                    head = "".join(f.readlines()[:20])
                context_parts.append(f"\n--- {config_file} (first 20 lines) ---\n{head}")
                break  # Only read one config file
            except Exception:
                pass

    if verbose:
        print(f"{Colors.DIM}Auto-context: {'; '.join(context_parts[:5])}{Colors.RESET}")

    return "\n".join(context_parts)
