"""File generation engine for TokenGuard.

Generates .claude/ directory structure with CLAUDE.md, rules, hooks,
memory scaffold, and tracking metadata.
"""

import hashlib
import json
from datetime import datetime, timezone
from importlib import resources
from pathlib import Path
from typing import Optional

from jinja2 import Environment, PackageLoader

from . import __version__
from .detector import detect_grip_global, has_grip_hook


def _hash_content(content: str) -> str:
    """SHA-256 hash of content for change tracking."""
    return hashlib.sha256(content.encode("utf-8")).hexdigest()[:16]


def _get_template_env() -> Environment:
    """Create Jinja2 template environment."""
    return Environment(
        loader=PackageLoader("tokenguard", "templates"),
        keep_trailing_newline=True,
        trim_blocks=True,
        lstrip_blocks=True,
    )


def _read_template_file(subdir: str, filename: str) -> str:
    """Read a static template file from the templates directory."""
    ref = resources.files("tokenguard") / "templates" / subdir / filename
    return ref.read_text(encoding="utf-8")


def generate_claude_md(
    project_type: str,
    grip_global: bool,
    project_name: Optional[str] = None,
) -> str:
    """Generate CLAUDE.md content.

    Args:
        project_type: Detected project type.
        grip_global: Whether full GRIP is installed globally.
        project_name: Optional project name for the header.

    Returns:
        Generated CLAUDE.md content.
    """
    env = _get_template_env()
    base = env.get_template("claude_md/base.md.j2")

    # Load type-specific additions
    type_template_name = f"claude_md/{project_type}.md.j2"
    try:
        type_additions = env.get_template(type_template_name).render()
    except Exception:
        type_additions = env.get_template("claude_md/generic.md.j2").render()

    return base.render(
        project_type=project_type,
        grip_global=grip_global,
        project_name=project_name or "Project",
        type_additions=type_additions,
        version=__version__,
    )


def generate_settings_json(hook_path: str) -> str:
    """Generate settings.json with hook registration.

    Args:
        hook_path: Relative path to the mirror hook script.

    Returns:
        JSON string for settings.json.
    """
    settings = {
        "$schema": "https://docs.anthropic.com/claude-code/settings.schema.json",
        "hooks": {
            "PreToolUse": [
                {
                    "matcher": ".*",
                    "hooks": [
                        {
                            "type": "command",
                            "command": f"python3 {hook_path}",
                            "statusMessage": "TokenGuard gate check",
                        }
                    ],
                }
            ]
        },
    }
    return json.dumps(settings, indent=2) + "\n"


def generate_mirror_json(
    project_type: str,
    grip_global: bool,
    file_hashes: dict,
    installed_components: list,
) -> str:
    """Generate .tokenguard.json tracking metadata.

    Args:
        project_type: Detected project type.
        grip_global: Whether GRIP is installed globally.
        file_hashes: Map of relative path -> content hash.
        installed_components: List of installed component names.

    Returns:
        JSON string for .tokenguard.json.
    """
    data = {
        "version": __version__,
        "project_type": project_type,
        "grip_global": grip_global,
        "installed_at": datetime.now(timezone.utc).isoformat(),
        "file_hashes": file_hashes,
        "installed_components": installed_components,
    }
    return json.dumps(data, indent=2) + "\n"


def scaffold_project(
    project_dir: Path,
    project_type: str,
    standalone: bool = False,
    no_hooks: bool = False,
    dry_run: bool = False,
) -> dict:
    """Generate all TokenGuard files for a project.

    Args:
        project_dir: Root directory of the target project.
        project_type: Detected or overridden project type.
        standalone: Force standalone mode even if GRIP detected globally.
        no_hooks: Skip hook installation.
        dry_run: Only return what would be created, don't write.

    Returns:
        Dict with keys:
            - files: dict of relative_path -> content
            - mode: "standalone" or "grip-user"
            - project_type: detected type
    """
    grip_global = detect_grip_global() and not standalone
    mode = "grip-user" if grip_global else "standalone"

    claude_dir = project_dir / ".claude"
    files = {}
    file_hashes = {}

    # CLAUDE.md (always generated)
    project_name = project_dir.name
    claude_md = generate_claude_md(project_type, grip_global, project_name)
    files[".claude/CLAUDE.md"] = claude_md
    file_hashes[".claude/CLAUDE.md"] = _hash_content(claude_md)

    # Memory scaffold (always generated)
    memory_content = _read_template_file("memory", "MEMORY.md.j2")
    files[".claude/memory/MEMORY.md"] = memory_content
    file_hashes[".claude/memory/MEMORY.md"] = _hash_content(memory_content)

    installed_components = ["claude-md", "memory"]

    if not grip_global:
        # Standalone mode: install rules
        rule_files = [
            "efficiency-rules.md",
            "bash-safety.md",
            "commit-standards.md",
            "session-protocol.md",
            "python-async-safety.md",
            "path-portability.md",
            "operational-principles.md",
        ]
        for rule_file in rule_files:
            content = _read_template_file("rules", rule_file)
            rel_path = f".claude/rules/{rule_file}"
            files[rel_path] = content
            file_hashes[rel_path] = _hash_content(content)
        installed_components.append("rules")

    # Check for existing GRIP hook — skip mirror hook if GRIP is already active
    grip_hook_active = has_grip_hook(project_dir) and not standalone
    hooks_skipped = False

    if not no_hooks and not grip_hook_active:
        # Install portable hook
        hook_content = _read_template_file("hooks", "mirror-hook.py")
        files[".claude/hooks/mirror-hook.py"] = hook_content
        file_hashes[".claude/hooks/mirror-hook.py"] = _hash_content(hook_content)
        installed_components.append("hooks")

        # Generate settings.json with hook registration
        settings = generate_settings_json(".claude/hooks/mirror-hook.py")
        files[".claude/settings.json"] = settings
        file_hashes[".claude/settings.json"] = _hash_content(settings)
        installed_components.append("settings")
    elif grip_hook_active:
        hooks_skipped = True

    # Generate .tokenguard.json
    mirror_json = generate_mirror_json(
        project_type, grip_global, file_hashes, installed_components
    )
    files[".claude/.tokenguard.json"] = mirror_json

    if not dry_run:
        for rel_path, content in files.items():
            full_path = project_dir / rel_path
            full_path.parent.mkdir(parents=True, exist_ok=True)
            full_path.write_text(content, encoding="utf-8")

    return {
        "files": files,
        "mode": mode,
        "project_type": project_type,
        "hooks_skipped": hooks_skipped,
    }
