#!/usr/bin/env python3
# CARL_HOOK_VERSION=1.0.0
"""
CLAUDE.md Re-injection Hook

Reinjects CLAUDE.md content when context drops below threshold.
Runs on UserPromptSubmit hook.

Logic:
1. Check if CLAUDEMD_REINJECT is enabled (session override or global)
2. Check if already injected this session (marker file)
3. Check context percentage (trigger at <=60% remaining)
4. Find CLAUDE.md (walk up from cwd)
5. Inject with marker to prevent re-injection
"""
import sys
import json
import subprocess
from pathlib import Path
from datetime import datetime

# Configuration
INJECTION_THRESHOLD = 60  # Inject when context <= 60% remaining
MAX_CONTEXT = 200000  # Claude's max context window

def get_context_percentage(input_data: dict) -> float | None:
    """
    Get current context percentage remaining.
    Uses same logic as CARL injector for consistency.
    """
    session_id = input_data.get('sessionId', '') or input_data.get('session_id', '')
    cwd = input_data.get('cwd', '')

    if not session_id or not cwd:
        return None

    home = str(Path.home())
    search_path = Path(cwd)
    session_file = None

    # Walk up directory tree to find session file
    for _ in range(10):
        project_dir = str(search_path).replace('/', '-').lstrip('-')
        candidate = Path(home) / '.claude' / 'projects' / f'-{project_dir}' / f'{session_id}.jsonl'
        if candidate.exists():
            session_file = candidate
            break
        if search_path.parent == search_path:
            break
        search_path = search_path.parent

    if session_file is None:
        return None

    try:
        result = subprocess.run(
            ['tail', '-20', str(session_file)],
            capture_output=True, text=True, timeout=5
        )

        latest_tokens = 0
        for line in result.stdout.strip().split('\n'):
            if not line:
                continue
            try:
                data = json.loads(line)
                # Use same structure as CARL: .message.usage.input_tokens
                usage = data.get('message', {}).get('usage', {})
                if usage:
                    input_tokens = usage.get('input_tokens', 0)
                    cache_tokens = usage.get('cache_read_input_tokens', 0)
                    tokens = input_tokens + cache_tokens
                    if tokens > 0:
                        latest_tokens = tokens
            except json.JSONDecodeError:
                continue

        if latest_tokens > 0:
            context_remaining = 100 - (latest_tokens * 100 // MAX_CONTEXT)
            return float(max(0, context_remaining))
    except Exception:
        pass

    return None


def find_carl_path(cwd: str) -> Path | None:
    """Find .carl directory by walking up from cwd."""
    search_path = Path(cwd)
    for _ in range(10):
        candidate = search_path / '.carl'
        if candidate.exists() and (candidate / 'manifest').exists():
            return candidate
        if search_path.parent == search_path:
            break
        search_path = search_path.parent
    return None


def load_session_config(carl_path: Path, session_id: str) -> dict | None:
    """Load session config from .carl/sessions/{uuid}.json."""
    if not session_id:
        return None
    session_file = carl_path / 'sessions' / f'{session_id}.json'
    if session_file.exists():
        try:
            return json.loads(session_file.read_text())
        except Exception:
            pass
    return None


def is_reinject_enabled(carl_path: Path, session_config: dict | None) -> bool:
    """Check if CLAUDEMD_REINJECT is enabled (session override or global)."""
    # Check session override first
    if session_config:
        override = session_config.get('overrides', {}).get('CLAUDEMD_REINJECT')
        if override is not None:
            return override

    # Check global manifest
    manifest_path = carl_path / 'manifest'
    if manifest_path.exists():
        try:
            content = manifest_path.read_text()
            for line in content.split('\n'):
                if line.startswith('CLAUDEMD_REINJECT='):
                    value = line.split('=', 1)[1].strip().lower()
                    return value == 'true'
        except Exception:
            pass

    # Default: enabled
    return True


def has_injection_marker(carl_path: Path, session_id: str) -> bool:
    """Check if injection marker exists for this session."""
    if not session_id:
        return False
    marker = carl_path / 'sessions' / f'{session_id}.claudemd-injected'
    return marker.exists()


def create_injection_marker(carl_path: Path, session_id: str):
    """Create injection marker to prevent re-injection."""
    if not session_id:
        return
    sessions_dir = carl_path / 'sessions'
    sessions_dir.mkdir(parents=True, exist_ok=True)
    marker = sessions_dir / f'{session_id}.claudemd-injected'
    marker.write_text(datetime.now().isoformat())


def find_claude_md(cwd: str) -> Path | None:
    """Find CLAUDE.md by walking up from cwd."""
    search_path = Path(cwd)
    for _ in range(10):
        candidate = search_path / 'CLAUDE.md'
        if candidate.exists():
            return candidate
        if search_path.parent == search_path:
            break
        search_path = search_path.parent

    # Also check home directory
    home_claude = Path.home() / '.claude' / 'CLAUDE.md'
    if home_claude.exists():
        return home_claude

    return None


def main():
    # Read input from stdin
    try:
        input_data = json.loads(sys.stdin.read())
    except json.JSONDecodeError:
        return

    session_id = input_data.get('sessionId', '') or input_data.get('session_id', '')
    cwd = input_data.get('cwd', '')

    if not cwd:
        return

    # Find .carl directory
    carl_path = find_carl_path(cwd)
    if not carl_path:
        return

    # Load session config
    session_config = load_session_config(carl_path, session_id)

    # Check if reinject is enabled
    if not is_reinject_enabled(carl_path, session_config):
        return

    # Check if already injected this session
    if has_injection_marker(carl_path, session_id):
        return

    # Check context percentage
    context_remaining = get_context_percentage(input_data)
    if context_remaining is None:
        return  # Can't determine, don't inject yet

    if context_remaining > INJECTION_THRESHOLD:
        return  # Still have plenty of context

    # Find CLAUDE.md
    claude_md_path = find_claude_md(cwd)
    if not claude_md_path:
        return

    # Read CLAUDE.md content
    try:
        content = claude_md_path.read_text()
    except Exception:
        return

    # Create injection marker
    create_injection_marker(carl_path, session_id)

    # Build reinforcement content
    reinforcement = f"""<claude-md-reinforcement>
## CLAUDE.md Reinforcement (Context at {context_remaining:.0f}%)

The following CLAUDE.md content is being re-injected to reinforce project context:

---

{content}

---
</claude-md-reinforcement>"""

    # Output using proper JSON hookSpecificOutput format (matches CARL injector)
    output = {
        "hookSpecificOutput": {
            "hookEventName": "UserPromptSubmit",
            "additionalContext": reinforcement
        }
    }
    print(json.dumps(output))


if __name__ == '__main__':
    main()
