import os
import signal
import time
from pathlib import Path

import click
from rich.console import Console
from rich.table import Table

from oghma import __version__
from oghma.config import (
    create_default_config,
    get_config_path,
    load_config,
    validate_config,
)
from oghma.daemon import Daemon, get_daemon_pid
from oghma.embedder import EmbedConfig, create_embedder
from oghma.exporter import Exporter, ExportOptions
from oghma.migration import EmbeddingMigration
from oghma.storage import Storage

console = Console()


@click.group()
@click.version_option(version=__version__, prog_name="oghma")
def cli() -> None:
    pass


@cli.command()
def init() -> None:
    config_path = get_config_path()

    if config_path.exists():
        console.print(f"[yellow]Config already exists at {config_path}[/yellow]")
        if not click.confirm("Overwrite existing config?"):
            console.print("[green]Init cancelled[/green]")
            return

    console.print("[blue]Creating Oghma configuration...[/blue]")
    config = create_default_config()
    console.print(f"[green]Config created at {config_path}[/green]")
    console.print(f"[cyan]Database path: {config['storage']['db_path']}[/cyan]")
    console.print("\n[yellow]Run 'oghma status' to verify setup[/yellow]")


@cli.command()
def status() -> None:
    try:
        config_path = get_config_path()
        config = load_config()
        db_path = config["storage"]["db_path"]
        pid_file = config["daemon"]["pid_file"]

        table = Table(title="Oghma Status", show_header=True, header_style="bold magenta")
        table.add_column("Property", style="cyan")
        table.add_column("Value", style="green")

        table.add_row("Config Path", str(config_path))

        pid = get_daemon_pid(pid_file)
        if pid:
            table.add_row("Daemon Status", f"[green]Running (PID: {pid})[/green]")
        else:
            table.add_row("Daemon Status", "[red]Stopped[/red]")

        table.add_row("Database Path", db_path)

        if Path(db_path).exists():
            storage = Storage(db_path, config)
            memory_count = storage.get_memory_count()
            table.add_row("Memory Count", str(memory_count))

            logs = storage.get_recent_extraction_logs(limit=1)
            if logs:
                last_extraction = logs[0]["created_at"]
                table.add_row("Last Extraction", last_extraction)
            else:
                table.add_row("Last Extraction", "Never")

            table.add_row("Database Status", "[green]Exists[/green]")

            from oghma.watcher import Watcher

            watcher = Watcher(config, storage)
            watched_files = watcher.discover_files()
            table.add_row("Watched Files", str(len(watched_files)))
        else:
            table.add_row("Memory Count", "0")
            table.add_row("Last Extraction", "Never")
            table.add_row("Database Status", "[yellow]Not created yet[/yellow]")
            table.add_row("Watched Files", "0")

        console.print(table)

        errors = validate_config(config)
        if errors:
            console.print("\n[red]Configuration errors:[/red]")
            for error in errors:
                console.print(f"  [red]- {error}[/red]")

    except FileNotFoundError:
        console.print("[red]Config not found. Run 'oghma init' first.[/red]")
    except Exception as e:
        console.print(f"[red]Error: {e}[/red]")


@cli.command()
@click.option("--foreground", "-f", is_flag=True, help="Run in foreground (don't daemonize)")
def start(foreground: bool) -> None:
    try:
        config = load_config()
        pid_file = config["daemon"]["pid_file"]

        pid = get_daemon_pid(pid_file)
        if pid:
            console.print(f"[red]Daemon already running (PID: {pid})[/red]")
            console.print("Use 'oghma stop' to stop it first.")
            raise SystemExit(1)

        console.print("[blue]Starting Oghma daemon...[/blue]")

        if not foreground:
            try:
                pid = os.fork()
                if pid > 0:
                    console.print(f"[green]Daemon started in background (PID: {pid})[/green]")
                    return
            except OSError as e:
                console.print(f"[yellow]Fork failed: {e}. Running in foreground.[/yellow]")

        daemon = Daemon(config)
        daemon.start()

    except FileNotFoundError:
        console.print("[red]Config not found. Run 'oghma init' first.[/red]")
        raise SystemExit(1) from None
    except Exception as e:
        console.print(f"[red]Error starting daemon: {e}[/red]")
        raise SystemExit(1) from None


@cli.command()
def stop() -> None:
    try:
        config = load_config()
        pid_file = config["daemon"]["pid_file"]

        pid = get_daemon_pid(pid_file)
        if not pid:
            console.print("[yellow]Daemon is not running[/yellow]")
            return

        console.print(f"[blue]Stopping daemon (PID: {pid})...[/blue]")

        try:
            os.kill(pid, signal.SIGTERM)
        except ProcessLookupError:
            console.print("[yellow]Daemon process not found. Cleaning up PID file.[/yellow]")
            Path(pid_file).unlink(missing_ok=True)
            return

        for _ in range(10):
            time.sleep(0.5)
            if not get_daemon_pid(pid_file):
                console.print("[green]Daemon stopped successfully[/green]")
                return

        console.print("[yellow]Daemon did not stop gracefully. Sending SIGKILL...[/yellow]")
        try:
            os.kill(pid, signal.SIGKILL)
        except ProcessLookupError:
            pass

        Path(pid_file).unlink(missing_ok=True)
        console.print("[green]Daemon force stopped[/green]")

    except FileNotFoundError:
        console.print("[red]Config not found. Run 'oghma init' first.[/red]")
        raise SystemExit(1) from None
    except Exception as e:
        console.print(f"[red]Error stopping daemon: {e}[/red]")
        raise SystemExit(1) from None


@cli.command()
@click.argument("query")
@click.option("--limit", "-n", default=10, help="Max results")
@click.option("--category", "-c", help="Filter by category")
@click.option(
    "--mode",
    type=click.Choice(["keyword", "vector", "hybrid"]),
    default="keyword",
    show_default=True,
    help="Search strategy",
)
def search(query: str, limit: int, category: str | None, mode: str) -> None:
    try:
        config = load_config()
        storage = Storage(config=config)
        query_embedding: list[float] | None = None

        if mode in {"vector", "hybrid"}:
            embed_config = config.get("embedding", {})
            embedder = create_embedder(EmbedConfig.from_dict(embed_config))
            query_embedding = embedder.embed(query)

        results = storage.search_memories_hybrid(
            query=query,
            query_embedding=query_embedding,
            limit=limit,
            category=category,
            search_mode=mode,
        )

        if not results:
            console.print(f"[yellow]No memories found matching: {query}[/yellow]")
            return

        console.print(f"[cyan]Found {len(results)} memories matching: {query}[/cyan]\n")

        for idx, memory in enumerate(results, 1):
            table = Table(show_header=False, box=None, padding=(0, 0))
            table.add_column("", style="cyan")
            table.add_column("")

            table.add_row(f"[bold]#{idx}[/bold]", f"[dim]{memory['created_at']}[/dim]")
            table.add_row("Category", f"[green]{memory['category']}[/green]")
            table.add_row("Source", f"{memory['source_tool']} ({Path(memory['source_file']).name})")
            table.add_row("Confidence", f"{memory['confidence']:.0%}")
            table.add_row("Content", memory["content"])

            console.print(table)
            console.print()

    except FileNotFoundError:
        console.print("[red]Config not found. Run 'oghma init' first.[/red]")
        raise SystemExit(1) from None
    except Exception as e:
        console.print(f"[red]Error searching memories: {e}[/red]")
        raise SystemExit(1) from None


@cli.command("migrate-embeddings")
@click.option("--batch-size", default=100, show_default=True, help="Batch size")
@click.option("--dry-run", is_flag=True, help="Preview migration without writing embeddings")
def migrate_embeddings(batch_size: int, dry_run: bool) -> None:
    try:
        config = load_config()
        storage = Storage(config=config)

        done_before, total = storage.get_embedding_progress()
        console.print(
            f"[blue]Embedding progress before migration:[/blue] {done_before}/{total} memories"
        )

        if done_before == total and total > 0:
            console.print("[green]All active memories already have embeddings.[/green]")
            return

        embed_config = config.get("embedding", {})
        embedder = create_embedder(EmbedConfig.from_dict(embed_config, batch_size=batch_size))

        migration = EmbeddingMigration(
            storage=storage,
            embedder=embedder,
            batch_size=batch_size,
        )
        result = migration.run(dry_run=dry_run)

        done_after, total_after = storage.get_embedding_progress()
        if dry_run:
            console.print(
                f"[yellow]Dry run complete.[/yellow] "
                f"Would process {result.processed} memories."
            )
            return

        console.print(
            "[green]Migration complete.[/green] "
            f"Processed={result.processed}, migrated={result.migrated}, "
            f"failed={result.failed}, skipped={result.skipped}"
        )
        console.print(
            f"[cyan]Embedding progress after migration:[/cyan] {done_after}/{total_after} memories"
        )
    except FileNotFoundError:
        console.print("[red]Config not found. Run 'oghma init' first.[/red]")
        raise SystemExit(1) from None
    except Exception as e:
        console.print(f"[red]Error migrating embeddings: {e}[/red]")
        raise SystemExit(1) from None


@cli.command()
@click.option("--output", "-o", type=click.Path(), help="Output directory")
@click.option("--format", "-f", type=click.Choice(["markdown", "json"]), default="markdown")
@click.option(
    "--group-by", "-g", type=click.Choice(["category", "date", "source"]), default="category"
)
@click.option("--category", "-c", help="Export only this category")
def export(output: str | None, format: str, group_by: str, category: str | None) -> None:
    """Export memories to files."""
    try:
        config = load_config()
        storage = Storage(config=config)

        output_dir = Path(output or config["export"]["output_dir"])

        options = ExportOptions(output_dir=output_dir, format=format, group_by=group_by)
        exporter = Exporter(storage, options)

        if category:
            console.print(f"[blue]Exporting memories for category: {category}[/blue]")
            file_path = exporter.export_category(category)
            console.print(f"[green]Exported to: {file_path}[/green]")
        else:
            console.print(f"[blue]Exporting memories (grouped by {group_by})...[/blue]")
            files = exporter.export()

            if not files:
                console.print("[yellow]No memories found to export[/yellow]")
                return

            for file_path in files:
                console.print(f"[green]Exported to: {file_path}[/green]")

    except ValueError as e:
        console.print(f"[red]Error: {e}[/red]")
        raise SystemExit(1) from None
    except FileNotFoundError:
        console.print("[red]Config not found. Run 'oghma init' first.[/red]")
        raise SystemExit(1) from None
    except Exception as e:
        console.print(f"[red]Error exporting memories: {e}[/red]")
        raise SystemExit(1) from None


def main() -> None:
    cli()
