"""Mind CLI - File-based memory for AI coding assistants (v2: daemon-free)."""

import json
from datetime import date
from pathlib import Path

import click

from . import __version__
from .legacy.context import update_claude_md
from .detection import detect_stack
from .legacy.parser import InlineScanner, Parser
from .storage import ProjectsRegistry, get_global_mind_dir, get_self_improve_path
from .config import create_default_config, load_config, save_config
from .templates import GITIGNORE_CONTENT, MEMORY_TEMPLATE, SESSION_TEMPLATE, REMINDERS_TEMPLATE, SELF_IMPROVE_TEMPLATE
from .preferences import (
    has_existing_preferences,
    load_global_preferences,
    save_global_preferences,
    get_default_preferences,
    update_last_project,
)
from .stack import detect_stack as detect_editor_stack, inject_mind_instructions, get_stack_display_name
from .health import auto_repair


@click.group()
@click.version_option(version=__version__, prog_name="mind")
def cli():
    """Mind - File-based memory for AI coding assistants.

    Quick start: mind init && mind status

    Common commands: init, status, search, doctor
    """
    pass


@cli.command()
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
@click.option("--quick", "-q", is_flag=True, help="Skip interactive setup, use defaults or existing preferences")
def init(path: str, quick: bool):
    """Initialize Mind for a project (creates .mind/ folder).

    PATH is the project directory to initialize. Defaults to current directory.

    Interactive setup asks 3 questions on first install. Use --quick to skip.

    When running from another directory via uv --directory, you MUST specify
    the target path explicitly:

        uv --directory /path/to/vibeship-mind run mind init /path/to/your/project
    """
    project_path = Path(path).resolve()

    # Warn if path is "." and we might be in the wrong directory
    # (common mistake when using uv --directory)
    if path == "." and project_path.name == "vibeship-mind":
        click.echo("Warning: Initializing vibeship-mind itself.", err=True)
        click.echo("If you meant to init a different project, specify the path:", err=True)
        click.echo("  uv --directory /path/to/vibeship-mind run mind init /path/to/your/project", err=True)
        click.echo()

    click.echo()
    click.echo("Welcome to Mind! Let me set things up for you.")
    click.echo()

    # Check for existing preferences
    existing_prefs = load_global_preferences()

    if existing_prefs and not quick:
        # Returning user - offer to reuse preferences
        click.echo("Found your Mind preferences from other projects:")
        click.echo(f"  - Logging: {existing_prefs.get('logging_level', 'balanced').title()}")
        click.echo(f"  - Auto-promote: {'Yes' if existing_prefs.get('auto_promote', True) else 'No'}")
        click.echo(f"  - Memory aging: {existing_prefs.get('retention_mode', 'smart').title()}")
        click.echo(f"  - Intelligence: {existing_prefs.get('intelligence_level', 'FREE')}")
        click.echo()

        reuse = click.confirm("Use these settings?", default=True)
        if reuse:
            prefs = existing_prefs
        else:
            prefs = _interactive_setup()
    elif quick and existing_prefs:
        # Quick mode with existing prefs
        prefs = existing_prefs
    elif quick:
        # Quick mode without existing prefs - use defaults
        prefs = get_default_preferences()
    else:
        # First install - interactive setup
        prefs = _interactive_setup()

    # Save preferences globally
    save_global_preferences(prefs)

    # Create .mind directory
    mind_dir = project_path / ".mind"
    mind_dir.mkdir(exist_ok=True)

    # Ensure global Mind directory exists with SELF_IMPROVE.md
    global_dir = get_global_mind_dir()
    self_improve_file = get_self_improve_path()
    if not self_improve_file.exists():
        self_improve_file.write_text(SELF_IMPROVE_TEMPLATE)
        click.echo(f"[+] Created global ~/.mind/SELF_IMPROVE.md")
    else:
        click.echo(f"[.] Global ~/.mind/SELF_IMPROVE.md already exists (preserved)")

    # Create .mind/.gitignore
    gitignore = mind_dir / ".gitignore"
    gitignore.write_text(GITIGNORE_CONTENT)

    # Detect tech stack
    stack = detect_stack(project_path)
    stack_str = ", ".join(stack) if stack else "(add your stack)"

    # Detect editor stack
    editor_stack = detect_editor_stack(project_path)
    editor_name = get_stack_display_name(editor_stack)

    # Create MEMORY.md (don't overwrite if exists)
    memory_file = mind_dir / "MEMORY.md"
    if not memory_file.exists():
        content = MEMORY_TEMPLATE.format(
            project_name=project_path.name,
            stack=stack_str,
            date=date.today().isoformat(),
        )
        memory_file.write_text(content)
        click.echo("[+] Created .mind/MEMORY.md")
    else:
        click.echo("[.] .mind/MEMORY.md already exists (preserved)")

    # Create SESSION.md (don't overwrite if exists)
    session_file = mind_dir / "SESSION.md"
    if not session_file.exists():
        session_content = SESSION_TEMPLATE.format(date=date.today().isoformat())
        session_file.write_text(session_content)
        click.echo("[+] Created .mind/SESSION.md")
    else:
        click.echo("[.] .mind/SESSION.md already exists (preserved)")

    # Create REMINDERS.md (don't overwrite if exists)
    reminders_file = mind_dir / "REMINDERS.md"
    if not reminders_file.exists():
        reminders_file.write_text(REMINDERS_TEMPLATE)
        click.echo("[+] Created .mind/REMINDERS.md")
    else:
        click.echo("[.] .mind/REMINDERS.md already exists (preserved)")

    # Create/update config.json with user preferences
    config_file = mind_dir / "config.json"
    if not config_file.exists():
        # Create new config with preferences
        config = {
            "version": 2,
            "mascot": True,
            "logging": {
                "level": prefs.get("logging_level", "balanced"),
                "auto_categorize": True,
            },
            "session": {
                "auto_promote": prefs.get("auto_promote", True),
                "promote_threshold": 0.5,
            },
            "memory": {
                "retention_mode": prefs.get("retention_mode", "smart"),
                "decay_period_days": 30,
                "decay_rate": 0.1,
                "min_relevance": 0.2,
            },
            "health": {
                "auto_repair": True,
            },
            "stack": {
                "detected": editor_stack,
                "config_file": str(inject_mind_instructions(project_path, editor_stack)["config_file"].name),
            },
            "self_improve": {
                "enabled": True,
                "decay": True,
                "reinforcement": True,
                "contradiction": True,
                "learning_style": True,
            },
        }
        save_config(project_path, config)
        click.echo("[+] Created .mind/config.json")
    else:
        click.echo("[.] .mind/config.json already exists (preserved)")

    # Inject Mind instructions into editor config file
    inject_result = inject_mind_instructions(project_path, editor_stack)
    if inject_result["success"]:
        if inject_result["action"] == "created":
            click.echo(f"[+] Added Mind instructions to {inject_result['config_file'].name}")
        elif inject_result["action"] == "updated":
            click.echo(f"[+] Updated Mind instructions in {inject_result['config_file'].name}")
        else:
            click.echo(f"[.] Mind instructions already in {inject_result['config_file'].name}")

    # Also update CLAUDE.md with MIND:CONTEXT (for backwards compatibility)
    update_claude_md(project_path, stack)
    click.echo("[+] Updated CLAUDE.md with MIND:CONTEXT")

    # Show detected stack
    click.echo(f"[+] Detected editor: {editor_name}")
    if stack:
        click.echo(f"[+] Detected tech stack: {', '.join(stack)}")
    else:
        click.echo("[.] No tech stack detected (update .mind/MEMORY.md manually)")

    # Update last project in global preferences
    update_last_project(project_path)

    # Register project
    registry = ProjectsRegistry.load()
    registry.register(project_path, stack)
    click.echo("[+] Registered project with Mind")

    # Run auto-repair to ensure everything is healthy
    repair_result = auto_repair(project_path)
    if repair_result["repaired_count"] > 0:
        click.echo(f"[+] Auto-repaired {repair_result['repaired_count']} issue(s)")

    click.echo()
    click.echo("Mind is ready! I'll remember what matters.")
    click.echo()
    click.echo("Settings:")
    click.echo(f"  - Logging: {prefs.get('logging_level', 'balanced').title()}")
    click.echo(f"  - Auto-promote: {'Yes' if prefs.get('auto_promote', True) else 'No'}")
    click.echo(f"  - Memory aging: {prefs.get('retention_mode', 'smart').title()}")
    click.echo(f"  - Intelligence: {prefs.get('intelligence_level', 'FREE')}")


def _interactive_setup() -> dict:
    """Run interactive setup questions and return preferences."""
    click.echo()

    # Question 1: Logging level
    click.echo("? How much should I remember?")
    logging_choices = [
        ("balanced", "Balanced - Key moments + context (recommended)"),
        ("efficient", "Efficient - Only critical decisions and blockers"),
        ("detailed", "Detailed - Everything, compacted to Memory periodically"),
    ]
    for i, (_, desc) in enumerate(logging_choices, 1):
        click.echo(f"  {i}. {desc}")

    logging_choice = click.prompt("Choose", type=click.IntRange(1, 3), default=1)
    logging_level = logging_choices[logging_choice - 1][0]
    click.echo()

    # Question 2: Auto-promote
    click.echo("? Should learnings auto-promote to long-term memory?")
    promote_choices = [
        (True, "Yes - Good insights move from Session to Memory automatically"),
        (False, "No - I'll decide what to keep"),
    ]
    for i, (_, desc) in enumerate(promote_choices, 1):
        click.echo(f"  {i}. {desc}")

    promote_choice = click.prompt("Choose", type=click.IntRange(1, 2), default=1)
    auto_promote = promote_choices[promote_choice - 1][0]
    click.echo()

    # Question 3: Retention mode
    click.echo("? How should memories age over time?")
    retention_choices = [
        ("smart", "Smart - Frequently-used memories stay strong, unused ones fade"),
        ("keep_all", "Keep all - Everything stays at full strength forever"),
    ]
    for i, (_, desc) in enumerate(retention_choices, 1):
        click.echo(f"  {i}. {desc}")

    retention_choice = click.prompt("Choose", type=click.IntRange(1, 2), default=1)
    retention_mode = retention_choices[retention_choice - 1][0]
    click.echo()

    # Question 4: Intelligence level (API integration)
    click.echo("? Enhanced Intelligence (optional)")
    click.echo("  Mind can use Claude API for smarter extraction,")
    click.echo("  session summaries, and semantic reranking.")
    click.echo()
    intelligence_choices = [
        ("FREE", "FREE - Local only, no API calls ($0/mo)"),
        ("LITE", "LITE - Haiku escalation for low-confidence (~$2/mo)"),
        ("BALANCED", "BALANCED - Haiku + Sonnet summaries (~$15/mo)"),
        ("PRO", "PRO - Full Haiku + Sonnet processing (~$40/mo)"),
        ("ULTRA", "ULTRA - Opus for deep synthesis (~$150/mo)"),
    ]
    for i, (_, desc) in enumerate(intelligence_choices, 1):
        click.echo(f"  {i}. {desc}")

    intelligence_choice = click.prompt("Choose", type=click.IntRange(1, 5), default=1)
    intelligence_level = intelligence_choices[intelligence_choice - 1][0]
    click.echo()

    # If non-FREE, check for API key
    api_key_set = False
    if intelligence_level != "FREE":
        import os
        existing_key = os.getenv("ANTHROPIC_API_KEY")
        if existing_key:
            click.echo("Found ANTHROPIC_API_KEY in environment.")
            api_key_set = True
        else:
            click.echo("Note: Set ANTHROPIC_API_KEY environment variable to enable API features.")
            click.echo("You can do this later in your shell config.")
        click.echo()

    return {
        "version": 1,
        "logging_level": logging_level,
        "auto_promote": auto_promote,
        "retention_mode": retention_mode,
        "intelligence_level": intelligence_level,
        "created": date.today().isoformat(),
    }


@cli.command()
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
@click.option("--inline", is_flag=True, help="Also scan code files for MEMORY: comments")
def parse(path: str, as_json: bool, inline: bool):
    """Parse MEMORY.md and show extracted entities."""
    project_path = Path(path).resolve()
    memory_file = project_path / ".mind" / "MEMORY.md"

    if not memory_file.exists():
        click.echo(f"Error: {memory_file} not found. Run 'mind init' first.", err=True)
        raise SystemExit(1)

    parser = Parser()
    content = memory_file.read_text(encoding="utf-8")
    result = parser.parse(content, source_file=str(memory_file))

    # Optionally scan inline comments
    inline_entities = []
    if inline:
        scanner = InlineScanner()
        inline_entities = scanner.scan_directory(project_path)

    if as_json:
        output = {
            "project_state": {
                "goal": result.project_state.goal,
                "stack": result.project_state.stack,
                "blocked_by": result.project_state.blocked_by,
            },
            "entities": [
                {
                    "type": e.type.value,
                    "title": e.title,
                    "content": e.content,
                    "confidence": e.confidence,
                    "reasoning": e.reasoning,
                    "status": e.status.value if e.status else None,
                    "date": e.date.isoformat() if e.date else None,
                    "source_file": e.source_file,
                    "source_line": e.source_line,
                }
                for e in result.entities + inline_entities
            ],
            "edges": [
                {"title": e.title, "workaround": e.workaround}
                for e in result.project_edges
            ],
        }
        click.echo(json.dumps(output, indent=2))
    else:
        # Human-readable output
        click.echo("=== Project State ===")
        click.echo(f"Goal: {result.project_state.goal or '(not set)'}")
        click.echo(f"Stack: {', '.join(result.project_state.stack) or '(not set)'}")
        click.echo(f"Blocked: {result.project_state.blocked_by or 'None'}")
        click.echo()

        if result.entities or inline_entities:
            click.echo("=== Entities ===")
            for e in result.entities + inline_entities:
                status_str = f" [{e.status.value}]" if e.status else ""
                date_str = f" ({e.date})" if e.date else ""
                confidence_str = f" [{e.confidence:.0%}]"
                click.echo(f"[{e.type.value}]{status_str}{confidence_str} {e.title}{date_str}")
                if e.reasoning:
                    click.echo(f"  Reason: {e.reasoning}")
                click.echo(f"  Source: {e.source_file}:{e.source_line}")
            click.echo()

        if result.project_edges:
            click.echo("=== Gotchas ===")
            for edge in result.project_edges:
                if edge.workaround:
                    click.echo(f"- {edge.title} -> {edge.workaround}")
                else:
                    click.echo(f"- {edge.title}")

        click.echo()
        total = len(result.entities) + len(inline_entities)
        click.echo(f"Total: {total} entities, {len(result.project_edges)} gotchas")


# Project management commands
@cli.command("list")
def list_projects():
    """List registered projects."""
    registry = ProjectsRegistry.load()
    projects = registry.list_all()

    if not projects:
        click.echo("No projects registered.")
        click.echo("Run 'mind init' in a project directory to register it.")
        return

    click.echo(f"Registered Projects ({len(projects)})")
    click.echo("-" * 40)

    for i, project in enumerate(projects, 1):
        click.echo(f"\n{i}. {project.name}")
        click.echo(f"   Path: {project.path}")
        if project.stack:
            click.echo(f"   Stack: {', '.join(project.stack)}")
        if project.last_activity:
            click.echo(f"   Last activity: {project.last_activity}")


@cli.command("add")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
def add_project(path: str):
    """Register a project with Mind."""
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    from .detection import detect_stack

    stack = detect_stack(project_path)
    registry = ProjectsRegistry.load()
    registry.register(project_path, stack)

    click.echo(f"Registered: {project_path.name}")


@cli.command("remove")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
def remove_project(path: str):
    """Unregister a project from Mind."""
    project_path = Path(path).resolve()

    registry = ProjectsRegistry.load()
    if registry.unregister(project_path):
        click.echo(f"Unregistered: {project_path.name}")
        click.echo("(Files in .mind/ preserved)")
    else:
        click.echo(f"Project not registered: {project_path.name}")


@cli.command("mcp")
def mcp_server():
    """Start MCP server (used by Claude Code, not typically run directly)."""
    from .mcp import run_server
    run_server()


def _check_project_health(project_path: Path, issues: list, warnings: list) -> None:
    """Check health of a single project and append issues/warnings."""
    from datetime import datetime, timedelta

    project_name = project_path.name
    mind_dir = project_path / ".mind"

    # Check .mind directory
    if not mind_dir.exists():
        click.echo(f"[!] No .mind/ in: {project_name}")
        issues.append(f"No .mind/ directory in {project_name}")
        return

    # Check MEMORY.md
    memory_file = mind_dir / "MEMORY.md"
    if not memory_file.exists():
        click.echo(f"[!] No MEMORY.md in: {project_name}")
        issues.append(f"No MEMORY.md in {project_name}")
        return

    # Check MEMORY.md is readable
    try:
        content = memory_file.read_text(encoding="utf-8")
        file_size_kb = memory_file.stat().st_size / 1024
        if file_size_kb > 100:
            click.echo(f"[.] {project_name}: MEMORY.md large ({file_size_kb:.0f}KB)")
            warnings.append(f"{project_name}: MEMORY.md is {file_size_kb:.0f}KB - consider archiving")
        else:
            click.echo(f"[+] {project_name}: MEMORY.md accessible ({file_size_kb:.0f}KB)")
    except Exception as e:
        click.echo(f"[!] {project_name}: Cannot read MEMORY.md")
        issues.append(f"Cannot read MEMORY.md in {project_name}: {e}")
        return

    # Check state.json
    state_file = mind_dir / "state.json"
    if state_file.exists():
        try:
            state = json.loads(state_file.read_text())
            if state.get("last_activity"):
                last = datetime.fromtimestamp(state["last_activity"] / 1000)
                age = datetime.now() - last
                if age > timedelta(days=7):
                    click.echo(f"[.] {project_name}: Last activity {age.days}d ago")
                    warnings.append(f"{project_name}: Last activity {age.days} days ago")
        except Exception:
            pass  # state.json parsing failed, continue

    # Check CLAUDE.md has MIND:CONTEXT
    claude_md = project_path / "CLAUDE.md"
    if claude_md.exists():
        claude_content = claude_md.read_text(encoding="utf-8")
        if "MIND:CONTEXT" in claude_content:
            click.echo(f"[+] {project_name}: MIND:CONTEXT present")
        else:
            click.echo(f"[.] {project_name}: No MIND:CONTEXT in CLAUDE.md")
            warnings.append(f"{project_name}: Run 'mind init' to add MIND:CONTEXT")
    else:
        click.echo(f"[.] {project_name}: No CLAUDE.md")
        warnings.append(f"{project_name}: No CLAUDE.md file")

    # v3 health checks
    v3_dir = mind_dir / "v3"
    graph_dir = v3_dir / "graph"
    if graph_dir.exists():
        try:
            from .v3.graph.store import GraphStore
            store = GraphStore(graph_dir)
            counts = store.get_counts()
            memory_count = store.memory_count()

            click.echo(f"[+] {project_name}: v3 Graph initialized")
            click.echo(f"    Memories: {memory_count}, Decisions: {counts.get('decisions', 0)}, "
                      f"Entities: {counts.get('entities', 0)}, Patterns: {counts.get('patterns', 0)}")

            # Check for empty tables (potential issue)
            if memory_count > 0 and counts.get('decisions', 0) == 0:
                click.echo(f"[.] {project_name}: v3 has memories but no extracted decisions")
                warnings.append(f"{project_name}: v3 decisions table empty - run 'mind migrate --force'")

            # Check migration status
            migration_marker = v3_dir / ".v3_migrated"
            if migration_marker.exists():
                click.echo(f"[+] {project_name}: v3 migration complete")
            elif memory_file.exists():
                click.echo(f"[.] {project_name}: v3 migration pending")
                warnings.append(f"{project_name}: Run 'mind migrate' to sync MEMORY.md to v3")

        except Exception as e:
            click.echo(f"[!] {project_name}: v3 Graph error: {e}")
            issues.append(f"v3 Graph error in {project_name}: {e}")
    else:
        # v3 not initialized - just a note, not a warning
        click.echo(f"[.] {project_name}: v3 not initialized (optional)")


@cli.command("doctor")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False), required=False)
def doctor(path: str):
    """Run health checks on Mind installation.

    If PATH is provided, checks that specific project.
    Otherwise checks the current directory and all registered projects.
    """
    from datetime import datetime, timedelta

    issues = []
    warnings = []

    click.echo("Mind Health Check (v3)")
    click.echo("-" * 40)

    # Check Mind home directory
    from .storage import get_mind_home

    mind_home = get_mind_home()
    if mind_home.exists():
        click.echo(f"[+] Config directory exists ({mind_home})")
    else:
        click.echo(f"[!] Config directory missing ({mind_home})")
        issues.append("Mind home directory not found")

    # Check current directory first (if it's a Mind project)
    current_path = Path(path).resolve()
    current_mind_dir = current_path / ".mind"
    checked_paths = set()

    if current_mind_dir.exists():
        click.echo()
        click.echo(f"Current project: {current_path.name}")
        _check_project_health(current_path, issues, warnings)
        checked_paths.add(str(current_path))

    # Check registered projects
    registry = ProjectsRegistry.load()
    projects = registry.list_all()
    click.echo()
    click.echo(f"[+] Projects registered: {len(projects)}")

    # Check each registered project (skip if already checked)
    for project in projects:
        project_path = Path(project.path)

        # Skip if already checked (e.g., current directory)
        if str(project_path) in checked_paths:
            continue

        # Check project exists
        if not project_path.exists():
            click.echo(f"[!] Project missing: {project.path}")
            issues.append(f"Project directory not found: {project.path}")
            continue

        _check_project_health(project_path, issues, warnings)

    # Check global edges
    from .mcp.server import load_global_edges

    global_edges = load_global_edges()
    click.echo(f"[+] Global edges loaded: {len(global_edges)}")

    # Summary
    click.echo()
    click.echo("-" * 40)

    if issues:
        click.echo(f"Issues ({len(issues)}):")
        for issue in issues:
            click.echo(f"  - {issue}")
        click.echo()

    if warnings:
        click.echo(f"Warnings ({len(warnings)}):")
        for warning in warnings:
            click.echo(f"  - {warning}")
        click.echo()

    if issues:
        click.echo("Overall: UNHEALTHY")
        raise SystemExit(1)
    elif warnings:
        click.echo(f"Overall: Healthy ({len(warnings)} warnings)")
    else:
        click.echo("Overall: Healthy")


@cli.command("patterns")
@click.option("--type", "pattern_type", type=click.Choice(["all", "preference", "skill", "blind_spot", "anti_pattern"]), default="all", help="Filter by pattern type")
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
def patterns(pattern_type: str, as_json: bool):
    """View learned patterns from SELF_IMPROVE.md."""
    from .self_improve import load_self_improve

    data = load_self_improve()

    # Select patterns based on type filter
    if pattern_type == "all":
        patterns_to_show = {
            "preferences": data.preferences,
            "skills": data.skills,
            "blind_spots": data.blind_spots,
            "anti_patterns": data.anti_patterns,
        }
    elif pattern_type == "preference":
        patterns_to_show = {"preferences": data.preferences}
    elif pattern_type == "skill":
        patterns_to_show = {"skills": data.skills}
    elif pattern_type == "blind_spot":
        patterns_to_show = {"blind_spots": data.blind_spots}
    elif pattern_type == "anti_pattern":
        patterns_to_show = {"anti_patterns": data.anti_patterns}
    else:
        patterns_to_show = {}

    if as_json:
        output = {}
        for key, plist in patterns_to_show.items():
            output[key] = [
                {"category": p.category, "description": p.description, "confidence": p.confidence}
                for p in plist
            ]
        click.echo(json.dumps(output, indent=2))
    else:
        total = 0
        for type_name, plist in patterns_to_show.items():
            if plist:
                # Format header: "preferences" -> "Preferences"
                header = type_name.replace("_", " ").title()
                click.echo(f"=== {header} ===")
                for p in plist:
                    click.echo(f"  [{p.category}] {p.description}")
                click.echo()
                total += len(plist)

        if total == 0:
            click.echo("No patterns found.")
            click.echo("Patterns are learned over time from your feedback.")
            click.echo()
            click.echo("Add patterns manually:")
            click.echo("  PREFERENCE: [category] description")
            click.echo("  SKILL: [stack:context] description")
            click.echo("  BLIND_SPOT: [category] description")
            click.echo("  ANTI_PATTERN: [category] description")
        else:
            click.echo(f"Total: {total} patterns")


@cli.command("feedback")
@click.option("--limit", default=20, help="Maximum entries to show")
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
def feedback(limit: int, as_json: bool):
    """View feedback log from SELF_IMPROVE.md."""
    from .self_improve import load_self_improve

    data = load_self_improve()
    entries = data.feedback[:limit]

    if as_json:
        output = [
            {"date": str(e.date_added), "description": e.description}
            for e in entries
        ]
        click.echo(json.dumps(output, indent=2))
    else:
        if not entries:
            click.echo("No feedback entries found.")
            click.echo()
            click.echo("Log feedback during sessions with:")
            click.echo("  mind_log('context -> correction', type='feedback')")
        else:
            click.echo(f"=== Feedback Log ({len(entries)}/{len(data.feedback)}) ===")
            for e in entries:
                click.echo(f"  [{e.date_added}] {e.description}")
            click.echo()
            if len(data.feedback) > limit:
                click.echo(f"({len(data.feedback) - limit} more entries, use --limit to see more)")


@cli.command("self")
def self_status():
    """Show SELF_IMPROVE.md summary and stats."""
    from .self_improve import load_self_improve, get_self_improve_path

    path = get_self_improve_path()
    if not path.exists():
        click.echo("SELF_IMPROVE.md not found.")
        click.echo("Run 'mind init' in any project to create it.")
        return

    data = load_self_improve()

    click.echo("=== Self-Improvement Summary ===")
    click.echo(f"Location: {path}")
    click.echo()

    # Stats
    click.echo("Patterns:")
    click.echo(f"  Preferences:   {len(data.preferences)}")
    click.echo(f"  Skills:        {len(data.skills)}")
    click.echo(f"  Blind Spots:   {len(data.blind_spots)}")
    click.echo(f"  Anti-Patterns: {len(data.anti_patterns)}")
    click.echo(f"  Feedback Log:  {len(data.feedback)} entries")
    click.echo()

    total = len(data.all_patterns())
    click.echo(f"Total: {total} patterns")

    # File size
    file_size_kb = path.stat().st_size / 1024
    click.echo(f"File size: {file_size_kb:.1f}KB")
    click.echo()

    # Quick preview of each type
    if data.blind_spots:
        click.echo("Recent Blind Spots (watch out!):")
        for bs in data.blind_spots[:3]:
            click.echo(f"  - [{bs.category}] {bs.description}")
        click.echo()

    if data.anti_patterns:
        click.echo("Recent Anti-Patterns (avoid!):")
        for ap in data.anti_patterns[:3]:
            click.echo(f"  - [{ap.category}] {ap.description}")


@cli.command("status")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
def status(path: str):
    """Show project status and stats."""
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    memory_file = mind_dir / "MEMORY.md"
    state_file = mind_dir / "state.json"

    click.echo(f"Project: {project_path.name}")
    click.echo("-" * 40)

    # Parse MEMORY.md
    if memory_file.exists():
        parser = Parser()
        content = memory_file.read_text(encoding="utf-8")
        result = parser.parse(content, str(memory_file))

        click.echo(f"Stack: {', '.join(result.project_state.stack) or '(not set)'}")
        click.echo(f"Goal: {result.project_state.goal or '(not set)'}")
        click.echo(f"Blocked: {result.project_state.blocked_by or 'None'}")
        click.echo()

        # Count by type
        decisions = sum(1 for e in result.entities if e.type.value == "decision")
        issues_open = sum(1 for e in result.entities if e.type.value == "issue" and (not e.status or e.status.value == "open"))
        issues_resolved = sum(1 for e in result.entities if e.type.value == "issue" and e.status and e.status.value == "resolved")
        learnings = sum(1 for e in result.entities if e.type.value == "learning")

        click.echo("Stats:")
        click.echo(f"  Decisions: {decisions}")
        click.echo(f"  Issues (open): {issues_open}")
        click.echo(f"  Issues (resolved): {issues_resolved}")
        click.echo(f"  Learnings: {learnings}")
        click.echo(f"  Gotchas: {len(result.project_edges)}")
        click.echo()

        # File size
        file_size_kb = memory_file.stat().st_size / 1024
        click.echo(f"MEMORY.md: {file_size_kb:.1f}KB")

    # Load state
    if state_file.exists():
        import json
        from datetime import datetime
        try:
            state = json.loads(state_file.read_text())
            if state.get("last_activity"):
                last = datetime.fromtimestamp(state["last_activity"] / 1000)
                click.echo(f"Last activity: {last.isoformat()}")
        except Exception:
            pass  # state.json parsing failed, continue

    # v3 Graph Status
    graph_path = mind_dir / "v3" / "graph"
    if graph_path.exists():
        try:
            from .v3.graph.store import GraphStore
            store = GraphStore(graph_path)
            counts = store.get_counts()
            click.echo()
            click.echo("v3 Graph: ACTIVE")
            click.echo(f"  Memories: {counts.get('memories', 0)}")
            click.echo(f"  Decisions: {counts.get('decisions', 0)}")
            click.echo(f"  Entities: {counts.get('entities', 0)}")
            click.echo(f"  Patterns: {counts.get('patterns', 0)}")
        except Exception:
            click.echo()
            click.echo("v3 Graph: ERROR")
    else:
        click.echo()
        click.echo("v3 Graph: NOT INITIALIZED")
        click.echo("  Run 'mind migrate .' to enable v3 features")


@cli.command("generate-views")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
def generate_views(path: str):
    """Generate human-readable markdown views from the graph.

    Creates DECISIONS.md, PATTERNS.md, and POLICIES.md in the .mind directory.
    """
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    try:
        from .v3.graph.store import GraphStore
        from .v3.views import ViewGenerator

        graph_path = mind_dir / "v3" / "graph"
        store = GraphStore(graph_path)
        if not store.is_initialized():
            click.echo("Graph store not initialized. No views to generate.")
            raise SystemExit(1)

        generator = ViewGenerator(store, mind_dir)
        paths = generator.generate_all()

        click.echo("Generated views:")
        for p in paths:
            click.echo(f"  {p.name}")

    except ImportError as e:
        click.echo(f"Error: v3 modules not available: {e}")
        raise SystemExit(1)


@cli.command("migrate")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
@click.option("--force", is_flag=True, help="Force re-migration even if already done")
def migrate_v3(path: str, force: bool):
    """Migrate MEMORY.md data to v3 structured tables.

    This command processes your existing MEMORY.md content and extracts:
    - Decisions (choices made and why)
    - Entities (files, functions, tools mentioned)
    - Patterns (preferences, habits, avoidances)

    Migration runs automatically on v3 init, but you can use this command
    to force re-processing or see detailed migration stats.
    """
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    try:
        from .v3.migration import migrate_project

        click.echo("Migrating MEMORY.md to v3 structured tables...")
        stats = migrate_project(project_path, force=force)

        click.echo()
        click.echo("Migration complete:")
        click.echo(f"  Memories processed: {stats.memories_processed}")
        click.echo(f"  Decisions extracted: {stats.decisions_added}")
        click.echo(f"  Entities extracted: {stats.entities_added}")
        click.echo(f"  Patterns extracted: {stats.patterns_added}")

        if stats.errors:
            click.echo()
            click.echo(f"  Warnings: {len(stats.errors)}")
            for err in stats.errors[:5]:  # Show first 5 errors
                click.echo(f"    - {err[:80]}")
            if len(stats.errors) > 5:
                click.echo(f"    ... and {len(stats.errors) - 5} more")

        click.echo()
        click.echo("Run 'mind generate-views' to create human-readable markdown files.")

    except ImportError as e:
        click.echo(f"Error: v3 modules not available: {e}")
        raise SystemExit(1)


@cli.command("sync")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
def sync_v3(path: str):
    """Incrementally sync MEMORY.md to v3 (faster than migrate).

    Unlike 'mind migrate', this only processes new entries that
    haven't been synced yet. Use this for regular syncs.

    Use 'mind migrate --force' to completely re-process everything.
    """
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    try:
        from .v3.graph.store import GraphStore
        from .v3.migration import MigrationManager

        graph_path = mind_dir / "v3" / "graph"
        store = GraphStore(graph_path)
        manager = MigrationManager(project_path, store)

        click.echo("Syncing MEMORY.md to v3...")
        stats = manager.sync_incremental()

        click.echo()
        click.echo("Sync complete:")
        click.echo(f"  Entries scanned: {stats.memories_processed}")
        click.echo(f"  New decisions: {stats.decisions_added}")
        click.echo(f"  New entities: {stats.entities_added}")
        click.echo(f"  New patterns: {stats.patterns_added}")

        if stats.total_structured == 0:
            click.echo()
            click.echo("No new content to sync (already up to date).")
        elif stats.errors:
            click.echo()
            click.echo(f"  Warnings: {len(stats.errors)}")

    except ImportError as e:
        click.echo(f"Error: v3 modules not available: {e}")
        raise SystemExit(1)


@cli.command("search")
@click.argument("query")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
@click.option("--limit", "-n", default=10, help="Maximum results to show")
@click.option("--type", "search_type", type=click.Choice(["all", "memories", "decisions", "entities"]), default="all", help="Type of content to search")
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
def search_v3(query: str, path: str, limit: int, search_type: str, as_json: bool):
    """Search memories using v3 semantic search.

    Uses real embeddings (sentence-transformers) to find semantically
    similar content, not just keyword matches.

    Examples:
        mind search "authentication flow"
        mind search "database migration" --type decisions
        mind search "React hooks" -n 5
    """
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    graph_path = mind_dir / "v3" / "graph"
    if not graph_path.exists():
        click.echo("v3 graph not initialized.")
        click.echo("Run 'mind migrate .' to enable semantic search.")
        raise SystemExit(1)

    try:
        from .v3.graph.store import GraphStore

        store = GraphStore(graph_path)
        results = []

        # Search based on type
        if search_type in ("all", "memories"):
            memories = store.search_memories(query, limit=limit)
            for m in memories:
                results.append({
                    "type": "memory",
                    "content": m.get("content", ""),
                    "memory_type": m.get("memory_type", ""),
                    "score": m.get("_distance", 0),
                })

        if search_type in ("all", "decisions"):
            decisions = store.search_decisions(query, limit=limit)
            for d in decisions:
                results.append({
                    "type": "decision",
                    "content": d.get("action", ""),
                    "reasoning": d.get("reasoning", ""),
                    "score": d.get("_distance", 0),
                })

        if search_type in ("all", "entities"):
            entities = store.search_entities(query, limit=limit)
            for e in entities:
                results.append({
                    "type": "entity",
                    "content": e.get("name", ""),
                    "entity_type": e.get("entity_type", ""),
                    "score": e.get("_distance", 0),
                })

        # Sort by score (lower distance = better match)
        results.sort(key=lambda x: x.get("score", float("inf")))
        results = results[:limit]

        if as_json:
            click.echo(json.dumps(results, indent=2))
        else:
            if not results:
                click.echo(f"No results for: {query}")
                return

            click.echo(f"Search: \"{query}\"")
            click.echo("-" * 40)

            for i, r in enumerate(results, 1):
                type_badge = f"[{r['type']}]"
                content = r["content"][:80] + "..." if len(r["content"]) > 80 else r["content"]

                if r["type"] == "memory":
                    subtype = f" ({r['memory_type']})" if r["memory_type"] else ""
                    click.echo(f"{i}. {type_badge}{subtype} {content}")
                elif r["type"] == "decision":
                    click.echo(f"{i}. {type_badge} {content}")
                    if r.get("reasoning"):
                        reason = r["reasoning"][:60] + "..." if len(r["reasoning"]) > 60 else r["reasoning"]
                        click.echo(f"   Reason: {reason}")
                elif r["type"] == "entity":
                    etype = f" ({r['entity_type']})" if r["entity_type"] else ""
                    click.echo(f"{i}. {type_badge}{etype} {content}")

            click.echo()
            click.echo(f"Found {len(results)} results")

    except ImportError as e:
        click.echo(f"Error: v3 modules not available: {e}")
        raise SystemExit(1)
    except Exception as e:
        click.echo(f"Search error: {e}")
        raise SystemExit(1)


@cli.command("config")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
@click.option("--set", "set_option", multiple=True, help="Set config option (key=value)")
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
def config_cmd(path: str, set_option: tuple, as_json: bool):
    """View or modify Mind configuration.

    Shows current configuration including intelligence level, API status,
    and feature settings.

    Examples:
        mind config                           # Show current config
        mind config --set intelligence_level=BALANCED
        mind config --json
    """
    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    # Handle setting options
    if set_option:
        config = load_config(project_path)
        for opt in set_option:
            if "=" not in opt:
                click.echo(f"Error: Invalid option format: {opt}")
                click.echo("Use: --set key=value")
                raise SystemExit(1)

            key, value = opt.split("=", 1)
            key = key.strip()
            value = value.strip()

            if key == "intelligence_level":
                valid_levels = ["FREE", "LITE", "BALANCED", "PRO", "ULTRA"]
                if value.upper() not in valid_levels:
                    click.echo(f"Error: Invalid intelligence level: {value}")
                    click.echo(f"Valid levels: {', '.join(valid_levels)}")
                    raise SystemExit(1)
                config["intelligence_level"] = value.upper()
                click.echo(f"Set intelligence_level = {value.upper()}")
            elif key == "logging_level":
                valid_levels = ["minimal", "balanced", "verbose"]
                if value.lower() not in valid_levels:
                    click.echo(f"Error: Invalid logging level: {value}")
                    click.echo(f"Valid levels: {', '.join(valid_levels)}")
                    raise SystemExit(1)
                config["logging_level"] = value.lower()
                click.echo(f"Set logging_level = {value.lower()}")
            else:
                click.echo(f"Warning: Unknown config key: {key}")
                config[key] = value
                click.echo(f"Set {key} = {value}")

        save_config(project_path, config)
        click.echo()
        click.echo("Configuration saved.")
        return

    # Display current config
    config = load_config(project_path)
    prefs = load_global_preferences() or {}

    # Get API status
    import os
    api_key_set = bool(os.environ.get("ANTHROPIC_API_KEY"))
    intelligence_level = config.get("intelligence_level") or prefs.get("intelligence_level", "FREE")

    output = {
        "project": str(project_path),
        "intelligence_level": intelligence_level,
        "api_key_configured": api_key_set,
        "logging_level": config.get("logging_level") or prefs.get("logging_level", "balanced"),
        "auto_promote": config.get("auto_promote") if "auto_promote" in config else prefs.get("auto_promote", True),
        "retention_mode": config.get("retention_mode") or prefs.get("retention_mode", "smart"),
    }

    # Check v3 status
    graph_path = mind_dir / "v3" / "graph"
    output["v3_enabled"] = graph_path.exists()

    if as_json:
        click.echo(json.dumps(output, indent=2))
    else:
        click.echo(f"Project: {project_path.name}")
        click.echo("-" * 40)
        click.echo(f"Intelligence Level: {output['intelligence_level']}")
        click.echo(f"API Key Configured: {'Yes' if output['api_key_configured'] else 'No (set ANTHROPIC_API_KEY)'}")
        click.echo(f"Logging Level: {output['logging_level']}")
        click.echo(f"Auto-Promote: {'Yes' if output['auto_promote'] else 'No'}")
        click.echo(f"Retention Mode: {output['retention_mode']}")
        click.echo(f"v3 Graph: {'Enabled' if output['v3_enabled'] else 'Disabled'}")

        if intelligence_level != "FREE" and not api_key_set:
            click.echo()
            click.echo("⚠️  Set ANTHROPIC_API_KEY to enable AI features")


@cli.command("synthesize")
@click.argument("path", default=".", type=click.Path(exists=True, file_okay=False))
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
def synthesize_cmd(path: str, as_json: bool):
    """Generate AI-powered session summary.

    Uses the Claude API to analyze the current session and extract:
    - Key decisions made
    - Learnings discovered
    - Unresolved items

    Requires ANTHROPIC_API_KEY and a non-FREE intelligence level.

    Examples:
        mind synthesize              # Synthesize current session
        mind synthesize --json       # Output as JSON
    """
    import asyncio
    import os

    project_path = Path(path).resolve()
    mind_dir = project_path / ".mind"

    if not mind_dir.exists():
        click.echo(f"Error: {project_path} is not a Mind project.")
        click.echo("Run 'mind init' first.")
        raise SystemExit(1)

    # Check API key
    if not os.environ.get("ANTHROPIC_API_KEY"):
        click.echo("Error: ANTHROPIC_API_KEY not set.")
        click.echo("Set your API key to use session synthesis.")
        raise SystemExit(1)

    # Check intelligence level
    config = load_config(project_path)
    prefs = load_global_preferences() or {}
    intelligence_level = config.get("intelligence_level") or prefs.get("intelligence_level", "FREE")

    if intelligence_level == "FREE":
        click.echo("Error: Session synthesis requires a non-FREE intelligence level.")
        click.echo("Run 'mind config --set intelligence_level=BALANCED' to enable.")
        raise SystemExit(1)

    try:
        from .v3.bridge import get_v3_bridge

        bridge = get_v3_bridge(project_path)

        if not bridge._api_client or not bridge._api_client.enabled:
            click.echo("Error: API client not enabled.")
            click.echo("Check your ANTHROPIC_API_KEY and intelligence level.")
            raise SystemExit(1)

        click.echo("Synthesizing session...")

        # Run async synthesis
        summary = asyncio.run(bridge.finalize_session_async())

        if not summary:
            click.echo("No session data to synthesize.")
            click.echo("The session may be empty or synthesis failed.")
            return

        if as_json:
            output = {
                "summary": summary.summary,
                "decisions": summary.decisions,
                "learnings": summary.learnings,
                "unresolved": summary.unresolved,
            }
            click.echo(json.dumps(output, indent=2))
        else:
            click.echo()
            click.echo("Session Summary")
            click.echo("=" * 40)
            click.echo(summary.summary)
            click.echo()

            if summary.decisions:
                click.echo("Decisions:")
                for d in summary.decisions:
                    click.echo(f"  • {d}")

            if summary.learnings:
                click.echo()
                click.echo("Learnings:")
                for l in summary.learnings:
                    click.echo(f"  • {l}")

            if summary.unresolved:
                click.echo()
                click.echo("Unresolved:")
                for u in summary.unresolved:
                    click.echo(f"  • {u}")

            click.echo()
            click.echo("Session synthesis complete.")

    except ImportError as e:
        click.echo(f"Error: v3 modules not available: {e}")
        raise SystemExit(1)
    except Exception as e:
        click.echo(f"Synthesis error: {e}")
        raise SystemExit(1)


if __name__ == "__main__":
    cli()
