"""
CodeContext CLI - Query and manage project memories.

A click-based CLI for interacting with the CodeContext HTTP API.
Supports both human-readable and JSON output formats.

Usage:
    codecontext query "authentication flow"
    codecontext add --type bugfix --title "Fixed X" --fact "Details..."
    codecontext note "Fixed timezone by using make_naive=True"
    codecontext stats
    codecontext init --global  # Install for all projects
    codecontext init --project # Install for current project only
"""

import json
import os
import sys
import urllib.request
import urllib.error
from datetime import datetime
from pathlib import Path
from typing import Optional, Any

import click

# Import timeout constants and version from package
from .constants import CLI_TIMEOUT_SHORT, CLI_TIMEOUT_LONG
from . import __version__


# ============== CONFIGURATION ==============

DEFAULT_API_BASE = "http://localhost:8765"

MEMORY_TYPES = ["bugfix", "feature", "discovery", "decision", "refactor", "optimization", "gotcha", "resolution"]
CONFIDENCE_LEVELS = ["high", "medium", "low"]


# ============== HTTP CLIENT ==============

class APIClient:
    """Simple HTTP client for CodeContext API."""

    DEFAULT_TIMEOUT = CLI_TIMEOUT_SHORT  # 30 seconds
    LONG_TIMEOUT = CLI_TIMEOUT_LONG      # 600 seconds (10 min)

    def __init__(self, base_url: str = DEFAULT_API_BASE):
        self.base_url = base_url.rstrip("/")

    def _request(self, method: str, path: str, data: Optional[dict] = None, params: Optional[dict] = None, timeout: Optional[int] = None) -> dict:
        """Make HTTP request."""
        url = f"{self.base_url}{path}"
        request_timeout = timeout if timeout is not None else self.DEFAULT_TIMEOUT

        # Add query parameters
        if params:
            query_string = "&".join(f"{k}={v}" for k, v in params.items() if v is not None)
            if query_string:
                url = f"{url}?{query_string}"

        if data:
            body = json.dumps(data).encode('utf-8')
            req = urllib.request.Request(url, data=body, method=method)
            req.add_header('Content-Type', 'application/json')
        else:
            req = urllib.request.Request(url, method=method)

        try:
            with urllib.request.urlopen(req, timeout=request_timeout) as response:
                return json.loads(response.read().decode('utf-8'))
        except urllib.error.HTTPError as e:
            try:
                error_body = json.loads(e.read().decode('utf-8'))
                return {"error": error_body.get("detail", str(e)), "status_code": e.code}
            except Exception:
                return {"error": str(e), "status_code": e.code}
        except urllib.error.URLError as e:
            if "timed out" in str(e.reason).lower():
                return {"error": "timed out", "status_code": None}
            return {"error": f"Connection failed: {e.reason}", "status_code": None}
        except Exception as e:
            return {"error": str(e), "status_code": None}

    def get(self, path: str, params: Optional[dict] = None, timeout: Optional[int] = None) -> dict:
        return self._request("GET", path, params=params, timeout=timeout)

    def post(self, path: str, data: dict, params: Optional[dict] = None, timeout: Optional[int] = None) -> dict:
        return self._request("POST", path, data=data, params=params, timeout=timeout)


# ============== UTILITIES ==============

def detect_project() -> str:
    """Detect project from current working directory.

    Must be run from project root directory (same directory where
    Claude Code sessions run). Running from subdirectories will
    point to a different project.

    Uses the same path encoding as hooks:
    C:\\Users\\Sam\\project -> C--Users-Sam-project
    """
    # Check environment variable first
    if os.environ.get("CODECONTEXT_PROJECT"):
        return os.environ["CODECONTEXT_PROJECT"]

    # Encode the full cwd path (same as hooks do)
    cwd = str(Path.cwd().resolve())
    return cwd.replace("\\", "-").replace("/", "-").replace(":", "-")


def format_datetime(iso_string: str) -> str:
    """Format ISO datetime string for display."""
    if not iso_string:
        return "unknown"
    try:
        dt = datetime.fromisoformat(iso_string.replace("Z", "+00:00"))
        return dt.strftime("%b %d, %Y")
    except Exception:
        return iso_string[:10] if len(iso_string) >= 10 else iso_string


def output_result(data: Any, as_json: bool, formatter=None):
    """Output data in JSON or human-readable format."""
    if as_json:
        click.echo(json.dumps(data, indent=2))
    elif formatter:
        formatter(data)
    else:
        click.echo(data)


def check_error(result: dict) -> bool:
    """Check if result contains an error. Returns True if error found."""
    if "error" in result:
        click.secho(f"Error: {result['error']}", fg="red", err=True)
        return True
    return False


# ============== OUTPUT FORMATTERS ==============

def format_memories(data: dict):
    """Format memory search results for human reading."""
    results = data.get("results", [])
    if not results:
        click.echo("No memories found.")
        return

    count = data.get("count", len(results))
    click.echo(f"\nFound {count} memories:\n")

    for mem in results:
        # Type and title
        mem_type = mem.get("type", "unknown")
        title = mem.get("title", "Untitled")

        # Similarity/relevance
        similarity = mem.get("similarity") or mem.get("relevance_pct", 0)
        if similarity > 1:  # It's a percentage
            pct = similarity
        else:  # It's a 0-1 score
            pct = similarity * 100

        click.secho(f"[{mem_type}] ", fg="cyan", nl=False)
        click.echo(f"{title} ", nl=False)
        click.secho(f"({pct:.0f}% match)", fg="yellow")

        # Fact (truncated)
        fact = mem.get("fact", "")
        if len(fact) > 120:
            fact = fact[:117] + "..."
        click.echo(f"  {fact}")

        # Files
        files = mem.get("files", [])
        if files:
            click.secho(f"  Files: {', '.join(files)}", fg="bright_black")

        # Date
        created = mem.get("created_at", "")
        if created:
            click.secho(f"  Created: {format_datetime(created)}", fg="bright_black")

        click.echo()


def format_stats(data: dict):
    """Format statistics for human reading."""
    if "project" in data:
        click.echo(f"Project: {data['project']}")
        click.echo(f"Memories: {data.get('memory_count', 0)}")
        if data.get("db_path"):
            click.echo(f"Database: {data['db_path']}")
    else:
        click.echo(f"Base path: {data.get('base_path', 'unknown')}")
        click.echo(f"Model: {data.get('model', 'unknown')}")
        click.echo(f"Device: {data.get('device', 'unknown')}")
        click.echo(f"Projects: {data.get('project_count', 0)}")
        click.echo(f"Total memories: {data.get('total_memories', 0)}")


def format_health(data: dict):
    """Format health check for human reading."""
    click.secho("CodeContext Server Status", fg="green", bold=True)
    click.echo(f"  Status: {data.get('status', 'unknown')}")
    click.echo(f"  Model: {data.get('model', 'unknown')}")
    click.echo(f"  Device: {data.get('device', 'unknown')}")
    click.echo(f"  Projects: {data.get('project_count', 0)}")

    gpu = data.get("gpu", {})
    if gpu.get("available"):
        click.echo(f"  GPU: {gpu.get('name', 'unknown')}")
        if gpu.get("memory_total"):
            click.echo(f"  GPU Memory: {gpu['memory_total']}")


def format_projects(data: dict):
    """Format project list for human reading."""
    projects = data.get("projects", [])
    count = data.get("count", len(projects))

    click.echo(f"\nFound {count} projects:\n")
    for p in projects:
        name = p.get("name", "unknown")
        mem_count = p.get("memory_count", 0)
        click.echo(f"  {name}: {mem_count} memories")


def format_summary(data: dict):
    """Format project summary for human reading."""
    click.secho("Project Summary", fg="green", bold=True)
    click.echo()

    if not data.get("has_summary"):
        click.secho("No summary generated yet. Run 'summary-generate' to create one.", fg="yellow")
        return

    summary = data.get("summary", {})
    if isinstance(summary, str):
        click.echo(summary)
    elif isinstance(summary, dict):
        # New structure: summary text is in summary["summary"]
        if summary.get("summary"):
            click.echo(summary["summary"])
            click.echo()
            click.secho(f"Version: {summary.get('version', 'unknown')} | "
                       f"Memories: {summary.get('memory_count', 0)} | "
                       f"Generated: {summary.get('generated_at', 'unknown')[:19]}", dim=True)
        # Legacy structure support
        elif summary.get("overview"):
            click.echo(summary["overview"])
            if summary.get("tech_stack"):
                click.echo(f"\nTech Stack: {', '.join(summary['tech_stack'])}")
            if summary.get("recent_focus"):
                click.echo(f"\nRecent Focus:")
                for item in summary["recent_focus"][:5]:
                    click.echo(f"  - {item}")


# ============== SERVER AUTO-START ==============

def auto_start_server(base_url: str) -> bool:
    """
    Ensure the CodeContext server is running, starting it if necessary.

    This is called before any API command to handle the case when
    the CLI is used outside of a Claude Code session.

    Args:
        base_url: Server base URL

    Returns:
        True if server is running, False if failed to start
    """
    # Quick health check first
    try:
        req = urllib.request.Request(f"{base_url}/health")
        with urllib.request.urlopen(req, timeout=2) as response:
            if response.status == 200:
                return True
    except (urllib.error.URLError, urllib.error.HTTPError, OSError):
        pass

    # Server not responding - try to import and use server_state
    try:
        from server_state import ensure_server_running

        click.echo("Starting CodeContext server...", err=True)
        if ensure_server_running(base_url):
            click.echo("Server started successfully", err=True)
            return True
        else:
            click.secho("Failed to start server", fg="red", err=True)
            return False

    except ImportError:
        # server_state not available - can't auto-start
        click.secho(
            "Server not running. Start with: codecontext server",
            fg="yellow",
            err=True
        )
        return False


# ============== CLI GROUP ==============

@click.group()
@click.version_option(__version__, '-V', '--version', prog_name='codecontext', message='%(prog)s %(version)s')
@click.option('--project', '-p', default=None, help='Project name (auto-detected from cwd if not specified)')
@click.option('--url', '-u', default=DEFAULT_API_BASE, help='API base URL')
@click.option('--no-auto-start', is_flag=True, help='Disable automatic server startup')
@click.pass_context
def cli(ctx, project, url, no_auto_start):
    """CodeContext CLI - Query and manage project memories.

    A semantic memory system for storing and retrieving learnings,
    bug fixes, decisions, and discoveries from coding sessions.
    """
    ctx.ensure_object(dict)
    ctx.obj['project'] = project or detect_project()
    ctx.obj['client'] = APIClient(url)
    ctx.obj['base_url'] = url

    # Auto-start server if needed (unless --no-auto-start flag)
    if not no_auto_start:
        # Only auto-start for subcommands (not when showing help)
        if ctx.invoked_subcommand is not None:
            auto_start_server(url)


# ============== COMMANDS ==============

@cli.command()
@click.argument('query_text')
@click.option('--top-k', '-k', default=5, help='Number of results (1-20)')
@click.option('--type', '-t', 'type_filter', type=click.Choice(MEMORY_TYPES + ['all']), default='all', help='Filter by memory type')
@click.option('--min-score', default=0.3, help='Minimum similarity score (0-1)')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def query(ctx, query_text, top_k, type_filter, min_score, as_json):
    """Search memories semantically.

    Examples:
        codecontext query "authentication flow"
        codecontext query "datetime bugs" --type bugfix --top-k 10
        codecontext query "api design" --json
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    payload = {
        "query": query_text,
        "top_k": min(max(top_k, 1), 20),
        "project": project
    }

    if type_filter and type_filter != 'all':
        payload["type_filter"] = type_filter

    result = client.post("/query", payload)

    if check_error(result):
        sys.exit(1)

    output_result(result, as_json, format_memories)


@cli.command()
@click.option('--type', '-t', 'mem_type', required=True, type=click.Choice(MEMORY_TYPES), help='Memory type')
@click.option('--title', required=True, help='Short searchable title (max 100 chars)')
@click.option('--fact', required=True, help='The learning details')
@click.option('--files', default='', help='Comma-separated file paths')
@click.option('--confidence', default='medium', type=click.Choice(CONFIDENCE_LEVELS), help='Confidence level')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def add(ctx, mem_type, title, fact, files, confidence, as_json):
    """Add a new memory.

    Examples:
        codecontext add --type bugfix --title "Fixed timezone" --fact "Use make_naive=True"
        codecontext add --type decision --title "Chose FastAPI" --fact "Better async support" --files "server.py"
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    file_list = [f.strip() for f in files.split(',') if f.strip()]

    payload = {
        "type": mem_type,
        "title": title[:100],
        "fact": fact,
        "files": file_list,
        "project": project,
        "confidence": confidence
    }

    result = client.post("/memory", payload)

    if check_error(result):
        sys.exit(1)

    if as_json:
        click.echo(json.dumps(result, indent=2))
    else:
        status = result.get("status", "unknown")
        mem_id = result.get("id", "unknown")
        if status == "added":
            click.secho(f"[OK] Memory added: {mem_id}", fg="green")
        elif status == "conflict":
            click.secho(f"⚠ Conflict detected - flagged for review: {mem_id}", fg="yellow")
        elif status == "duplicate":
            click.secho(f"[FAIL] Duplicate - not added (similar memory exists)", fg="red")
        else:
            click.echo(f"Status: {status}, ID: {mem_id}")


@cli.command()
@click.argument('learning')
@click.option('--files', default='', help='Comma-separated file paths')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def note(ctx, learning, files, as_json):
    """Quick capture with auto-classification.

    Automatically determines the memory type based on content.

    Examples:
        codecontext note "Fixed timezone by using make_naive=True"
        codecontext note "Always use utcnow() for storage" --files "utils/datetime.py"
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    file_list = [f.strip() for f in files.split(',') if f.strip()]

    payload = {
        "learning": learning,
        "files": file_list,
        "project": project
    }

    result = client.post("/api/memory/quick", payload)

    if check_error(result):
        sys.exit(1)

    if as_json:
        click.echo(json.dumps(result, indent=2))
    else:
        mem_type = result.get("type", "discovery")
        title = result.get("title", learning[:50])
        click.secho(f"[OK] Note saved as [{mem_type}]: {title}", fg="green")


@cli.command()
@click.argument('description')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def resolve(ctx, description, as_json):
    """Mark a priority as resolved.

    Creates a 'resolution' memory that signals a previously open priority
    has been completed. This helps keep project summaries accurate by
    explicitly marking when issues are addressed.

    Examples:
        codecontext resolve "Implemented progressive summary generation"
        codecontext resolve "Fixed timestamp standardization in consolidation.py"
        codecontext resolve "Completed Phase 1 Skills implementation"
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    payload = {
        "type": "resolution",
        "title": f"Resolved: {description[:80]}",
        "fact": f"Priority completed: {description}. This issue/feature has been addressed and should no longer appear in active priorities.",
        "files": [],
        "project": project,
        "confidence": "high",
        "scope": "project"
    }

    result = client.post("/memory", payload)

    if check_error(result):
        sys.exit(1)

    if as_json:
        click.echo(json.dumps(result, indent=2))
    else:
        click.secho(f"[OK] Marked as resolved: {description}", fg="green")
        click.echo("  Run 'codecontext summary-generate --progressive' to update summary")


@cli.command()
@click.option('--include-timeline', is_flag=True, help='Include recent activity timeline')
@click.option('--include-stats', is_flag=True, help='Include memory statistics')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def summary(ctx, include_timeline, include_stats, as_json):
    """Get project summary.

    Shows high-level overview including tech stack, recent focus, and known gotchas.

    Examples:
        codecontext summary
        codecontext summary --include-timeline --json
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    params = {"project": project}
    if include_timeline:
        params["include_timeline"] = "true"
    if include_stats:
        params["include_stats"] = "true"

    result = client.get("/api/summary", params)

    if check_error(result):
        sys.exit(1)

    output_result(result, as_json, format_summary)


@cli.command("summary-generate")
@click.option('--force-full', is_flag=True, help='Regenerate from all memories (not incremental)')
@click.option('--progressive', is_flag=True, help='Use progressive time-based chunking (best for fixing stale priorities)')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def summary_generate(ctx, force_full, progressive, as_json):
    """Generate or regenerate project summary.

    Creates a new summary from all stored memories using AI.

    Modes:
        (default)     Incremental update with new memories only
        --force-full  Regenerate from all memories in fixed chunks
        --progressive Time-based chunking that keeps sessions together
                      (best for fixing stale "Next Priority" sections)

    Examples:
        codecontext summary-generate
        codecontext summary-generate --force-full --json
        codecontext summary-generate --progressive
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    params = {"project": project}
    if progressive:
        params["progressive"] = "true"
    elif force_full:
        params["force_full"] = "true"

    if not as_json:
        if progressive:
            click.echo("Generating progressive summary... (this may take up to 10 minutes)")
        elif force_full:
            click.echo("Generating full summary... (this may take a few minutes)")
        else:
            click.echo("Generating summary... (this may take a moment)")

    # Use long timeout for summary generation (can take several minutes)
    result = client.post("/api/summary/regenerate", data={}, params=params, timeout=APIClient.LONG_TIMEOUT)

    if check_error(result):
        sys.exit(1)

    if as_json:
        click.echo(json.dumps(result, indent=2))
    else:
        if result.get("success"):
            mode = "progressive" if progressive else ("full" if force_full else "incremental")
            click.secho(f"[OK] Summary generated (version {result.get('version')}, {mode})", fg="green")
            click.echo(f"  Processed {result.get('new_memories_count', 0)} memories")
            if result.get("chunks"):
                click.echo(f"  Chunks: {result.get('chunks')}")
        else:
            click.secho("[FAIL] Summary generation failed", fg="red")


@cli.command("files")
@click.argument('file_paths', nargs=-1, required=True)
@click.option('--match-mode', type=click.Choice(['exact', 'contains', 'fuzzy']), default='contains', help='How to match file paths')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def files_cmd(ctx, file_paths, match_mode, as_json):
    """Find memories related to specific files.

    Examples:
        codecontext files "utils/datetime.py"
        codecontext files "server.py" "client.py" --match-mode exact
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    payload = {
        "files": list(file_paths),
        "match_mode": match_mode,
        "project": project
    }

    result = client.post("/api/memories/by-files", payload)

    if check_error(result):
        sys.exit(1)

    output_result(result, as_json, format_memories)


@cli.command()
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def stats(ctx, as_json):
    """Show project statistics.

    Examples:
        codecontext stats
        codecontext stats --json
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    result = client.get("/stats", {"project": project})

    if check_error(result):
        sys.exit(1)

    output_result(result, as_json, format_stats)


@cli.command()
@click.option('--limit', '-n', default=10, help='Number of recent memories')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def recent(ctx, limit, as_json):
    """Show recent memories.

    Examples:
        codecontext recent
        codecontext recent --limit 20 --json
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    params = {
        "project": project,
        "limit": min(max(limit, 1), 50)
    }

    result = client.get("/api/memories/recent", params)

    if check_error(result):
        sys.exit(1)

    output_result(result, as_json, format_memories)


@cli.command()
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def gotchas(ctx, as_json):
    """List known gotchas and pitfalls.

    Examples:
        codecontext gotchas
        codecontext gotchas --json
    """
    client = ctx.obj['client']
    project = ctx.obj['project']

    # Query for gotcha type memories
    payload = {
        "query": "gotcha pitfall warning caveat",
        "top_k": 20,
        "project": project,
        "type_filter": "gotcha"
    }

    result = client.post("/query", payload)

    if check_error(result):
        sys.exit(1)

    if as_json:
        click.echo(json.dumps(result, indent=2))
    else:
        results = result.get("results", [])
        if not results:
            click.echo("No gotchas recorded yet.")
            return

        click.secho(f"\nKnown Gotchas ({len(results)}):\n", fg="yellow", bold=True)
        for i, mem in enumerate(results, 1):
            click.echo(f"{i}. {mem.get('title', 'Untitled')}")
            fact = mem.get('fact', '')
            if len(fact) > 100:
                fact = fact[:97] + "..."
            click.echo(f"   {fact}")
            click.echo()


@cli.command()
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def health(ctx, as_json):
    """Check server health.

    Examples:
        codecontext health
        codecontext health --json
    """
    client = ctx.obj['client']

    result = client.get("/health")

    if check_error(result):
        click.secho("Is the server running? Start with: codecontext server", fg="yellow", err=True)
        sys.exit(1)

    output_result(result, as_json, format_health)


@cli.command()
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
@click.pass_context
def projects(ctx, as_json):
    """List all projects.

    Examples:
        codecontext projects
        codecontext projects --json
    """
    client = ctx.obj['client']

    result = client.get("/projects")

    if check_error(result):
        sys.exit(1)

    output_result(result, as_json, format_projects)


# ============== INSTALLATION COMMANDS ==============

@cli.command()
@click.option('--global', 'install_global_flag', is_flag=True, help='Install globally for all projects')
@click.option('--project', 'install_project_flag', is_flag=True, help='Install for current project only')
@click.option('--yes', '-y', is_flag=True, help='Skip confirmation prompts')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
def init(install_global_flag, install_project_flag, yes, as_json):
    """Initialize CodeContext for Claude Code integration.

    Sets up hooks and skills for automatic memory capture.

    Examples:
        codecontext init              # Interactive mode
        codecontext init --global     # Install for all projects
        codecontext init --project    # Install for current project only
    """
    from .installer import (
        install_global,
        install_project,
        detect_existing_installation,
    )

    # Check for existing installation
    existing = detect_existing_installation()

    # Interactive mode if no flags provided
    if not install_global_flag and not install_project_flag:
        click.echo()
        click.secho("CodeContext Installation", fg="cyan", bold=True)
        click.echo()
        click.echo("  [1] Global  - Active for ALL projects (~/.claude/)")
        click.echo("  [2] Project - Active only for THIS project (./.claude/)")
        click.echo()

        # Show existing installation info
        if existing["has_global"]:
            click.secho("  Note: Global installation already exists", fg="yellow")
        if existing["has_project"]:
            click.secho("  Note: Project installation already exists in current directory", fg="yellow")

        choice = click.prompt("Select installation mode", type=click.Choice(['1', '2']))
        install_global_flag = (choice == '1')
        install_project_flag = (choice == '2')

    # Warn about existing data
    if install_global_flag and existing["global_data_exists"]:
        if not yes:
            click.secho("\nExisting data found at ~/.codecontext/", fg="yellow")
            if not click.confirm("Keep existing memories and data?", default=True):
                click.echo("Installation cancelled. Run with --yes to overwrite.")
                return

    if install_project_flag and existing["project_data_exists"]:
        if not yes:
            click.secho("\nExisting data found at ./.codecontext/", fg="yellow")
            if not click.confirm("Keep existing memories and data?", default=True):
                click.echo("Installation cancelled. Run with --yes to overwrite.")
                return

    # Perform installation
    try:
        if install_global_flag:
            results = install_global()
        else:
            results = install_project(Path.cwd())
    except Exception as e:
        if as_json:
            click.echo(json.dumps({"error": str(e)}, indent=2))
        else:
            click.secho(f"Installation failed: {e}", fg="red", err=True)
        sys.exit(1)

    # Output results
    if as_json:
        click.echo(json.dumps(results, indent=2))
    else:
        click.echo()
        click.secho("Installation complete!", fg="green", bold=True)
        click.echo()
        if results.get("backup_path"):
            click.echo(f"  Backup: {results['backup_path']}")
        click.echo(f"  Mode: {results['mode']}")
        click.echo(f"  Hooks: {results['hooks_dir']}")
        click.echo(f"  Skills: {results['skills_dir']}")
        click.echo(f"  Settings: {results['settings_path']}")
        click.echo()
        click.echo("  Hooks will activate in new Claude Code sessions.")
        click.echo("  Run 'codecontext health' to check server status.")


@cli.command()
@click.option('--global', 'uninstall_global_flag', is_flag=True, help='Remove global installation')
@click.option('--project', 'uninstall_project_flag', is_flag=True, help='Remove project installation')
@click.option('--keep-data', is_flag=True, default=True, help='Keep memories and data (default: True)')
@click.option('--remove-data', is_flag=True, help='Also remove all memories and data')
@click.option('--yes', '-y', is_flag=True, help='Skip confirmation prompts')
@click.option('--json', 'as_json', is_flag=True, help='Output as JSON')
def uninstall(uninstall_global_flag, uninstall_project_flag, keep_data, remove_data, yes, as_json):
    """Remove CodeContext hooks and settings.

    By default, preserves your memories and data. Use --remove-data to delete everything.

    Examples:
        codecontext uninstall --global         # Remove global, keep data
        codecontext uninstall --project        # Remove from current project
        codecontext uninstall --global --remove-data  # Remove everything
    """
    from .installer import (
        uninstall_global,
        uninstall_project,
        detect_existing_installation,
    )

    # Determine keep_data flag
    actual_keep_data = not remove_data

    # Interactive mode if no flags provided
    if not uninstall_global_flag and not uninstall_project_flag:
        existing = detect_existing_installation()

        click.echo()
        click.secho("CodeContext Uninstall", fg="cyan", bold=True)
        click.echo()
        click.echo("  [1] Global  - Remove from ~/.claude/")
        click.echo("  [2] Project - Remove from ./.claude/")
        click.echo()

        if not existing["has_global"] and not existing["has_project"]:
            click.secho("No CodeContext installation found.", fg="yellow")
            return

        choice = click.prompt("Select", type=click.Choice(['1', '2']))
        uninstall_global_flag = (choice == '1')
        uninstall_project_flag = (choice == '2')

    # Confirmation
    if not yes:
        mode = "global" if uninstall_global_flag else "project"
        if not click.confirm(f"Remove CodeContext {mode} installation?"):
            click.echo("Cancelled.")
            return

        if not actual_keep_data:
            click.secho("\nWARNING: This will delete all memories and data!", fg="red", bold=True)
            if not click.confirm("Are you sure you want to delete all data?"):
                click.echo("Cancelled.")
                return

    # Perform uninstall
    try:
        if uninstall_global_flag:
            results = uninstall_global(keep_data=actual_keep_data)
        else:
            results = uninstall_project(Path.cwd(), keep_data=actual_keep_data)
    except Exception as e:
        if as_json:
            click.echo(json.dumps({"error": str(e)}, indent=2))
        else:
            click.secho(f"Uninstall failed: {e}", fg="red", err=True)
        sys.exit(1)

    # Output results
    if as_json:
        click.echo(json.dumps(results, indent=2))
    else:
        click.echo()
        click.secho("Uninstall complete!", fg="green", bold=True)
        click.echo()
        click.echo(f"  Hooks removed: {results['hooks_removed']}")
        click.echo(f"  Skills removed: {results['skills_removed']}")
        click.echo(f"  Settings updated: {results['settings_updated']}")
        if actual_keep_data:
            click.echo(f"  Data preserved: {results['data_dir']}")
        else:
            click.echo(f"  Data removed: {results['data_removed']}")


@cli.command()
@click.option('--host', '-h', default='localhost', help='Host to bind to')
@click.option('--port', '-p', default=8765, help='Port to bind to')
@click.option('--reload', is_flag=True, help='Enable auto-reload for development')
def server(host, port, reload):
    """Start the CodeContext server.

    Examples:
        codecontext server
        codecontext server --port 9000
        codecontext server --reload  # Development mode
    """
    try:
        import uvicorn
        from .server import app

        click.secho(f"Starting CodeContext server on http://{host}:{port}", fg="cyan")
        uvicorn.run(
            "codecontext.server:app" if reload else app,
            host=host,
            port=port,
            reload=reload,
            log_level="warning",
        )
    except ImportError as e:
        click.secho(f"Missing dependency: {e}", fg="red", err=True)
        click.echo("Install with: pip install codecontext[full]")
        sys.exit(1)


# ============== MAIN ==============

def main():
    """Entry point for the CLI."""
    cli(obj={})


if __name__ == "__main__":
    main()
