"""
Database domain command handlers.

SST Reference: packages/lightwave-core/lightwave/schema/definitions/cli.yaml
Domain: db (runtime: python)

These commands provide database management utilities for development and production.
"""

from __future__ import annotations

import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any


def _get_database_url(env: str = "dev") -> str | None:
    """Get database URL from environment or settings."""
    # Check environment variable first
    db_url = os.environ.get("DATABASE_URL")
    if db_url:
        return db_url

    # Try Django settings
    try:
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
        import django

        django.setup()

        from django.conf import settings

        db_config = settings.DATABASES.get("default", {})
        if db_config:
            user = db_config.get("USER", "postgres")
            password = db_config.get("PASSWORD", "")
            host = db_config.get("HOST", "localhost")
            port = db_config.get("PORT", "5432")
            name = db_config.get("NAME", "lightwave")

            if password:
                return f"postgresql://{user}:{password}@{host}:{port}/{name}"
            return f"postgresql://{user}@{host}:{port}/{name}"
    except Exception:
        pass

    return None


def _find_project_root() -> Path | None:
    """Find the project root directory."""
    cwd = Path.cwd()

    # Look for common markers
    markers = ["manage.py", "pyproject.toml", "CLAUDE.md"]

    for parent in [cwd, *cwd.parents]:
        for marker in markers:
            if (parent / marker).exists():
                return parent

    return cwd


def db_shell(
    args: list[str],
    *,
    json_output: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Open database shell (psql).

    SST: domains.db.commands[name=shell]
    flags: [--env]
    """
    env = "dev"
    for i, arg in enumerate(args):
        if arg == "--env" and i + 1 < len(args):
            env = args[i + 1]
            break

    db_url = _get_database_url(env)

    if db_url:
        # Use psql with connection string
        cmd = ["psql", db_url]
    else:
        # Try docker exec into postgres container
        cmd = ["docker", "compose", "exec", "postgres", "psql", "-U", "postgres"]

    if verbose:
        print(f"Running: {' '.join(cmd)}")

    try:
        subprocess.run(cmd, check=True)
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)
    except FileNotFoundError:
        if json_output:
            return {"success": False, "error": "psql not found"}
        print("Error: psql not found. Install postgresql client.", file=sys.stderr)
        sys.exit(1)


def db_dump(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Dump database to file.

    SST: domains.db.commands[name=dump]
    args: [env]
    flags: [--output, --tables]
    """
    env = args[0] if args and not args[0].startswith("-") else "dev"

    # Parse output file
    output_file = None
    tables = None

    for i, arg in enumerate(args):
        if arg == "--output" and i + 1 < len(args):
            output_file = args[i + 1]
        elif arg == "--tables" and i + 1 < len(args):
            tables = args[i + 1].split(",")

    # Default output file
    if not output_file:
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        output_file = f"dump_{env}_{timestamp}.sql"

    db_url = _get_database_url(env)

    if dry_run:
        if not json_output:
            print(f"[dry-run] Would dump {env} database to {output_file}")
            if tables:
                print(f"  Tables: {', '.join(tables)}")
        return {
            "success": True,
            "dry_run": True,
            "env": env,
            "output": output_file,
            "tables": tables,
        }

    # Build pg_dump command
    cmd = ["pg_dump"]

    if db_url:
        cmd.append(db_url)
    else:
        # Default local connection
        cmd.extend(["-h", "localhost", "-U", "postgres", "-d", "lightwave"])

    cmd.extend(["-f", output_file])

    if tables:
        for table in tables:
            cmd.extend(["-t", table])

    if verbose:
        print(f"Running: {' '.join(cmd)}")

    try:
        subprocess.run(cmd, check=True)
        if json_output:
            return {
                "success": True,
                "message": f"Database dumped to {output_file}",
                "output": output_file,
            }
        print(f"Database dumped to {output_file}")
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)
    except FileNotFoundError:
        if json_output:
            return {"success": False, "error": "pg_dump not found"}
        print("Error: pg_dump not found. Install postgresql client.", file=sys.stderr)
        sys.exit(1)


def db_restore(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Restore database from dump.

    SST: domains.db.commands[name=restore]
    args: [file]
    flags: [--env, --confirm]
    """
    # Get dump file
    dump_file = None
    for arg in args:
        if not arg.startswith("-"):
            dump_file = arg
            break

    if not dump_file:
        if json_output:
            return {"success": False, "error": "Dump file required"}
        print("Error: Dump file required", file=sys.stderr)
        sys.exit(1)

    if not Path(dump_file).exists():
        if json_output:
            return {"success": False, "error": f"File not found: {dump_file}"}
        print(f"Error: File not found: {dump_file}", file=sys.stderr)
        sys.exit(1)

    env = "dev"
    confirm = "--confirm" in args

    for i, arg in enumerate(args):
        if arg == "--env" and i + 1 < len(args):
            env = args[i + 1]

    if dry_run:
        if not json_output:
            print(f"[dry-run] Would restore {dump_file} to {env} database")
        return {
            "success": True,
            "dry_run": True,
            "file": dump_file,
            "env": env,
        }

    # Safety check for production
    if env == "prod" and not confirm:
        if json_output:
            return {"success": False, "error": "Use --confirm for production restores"}
        print("Error: Use --confirm flag for production database restores", file=sys.stderr)
        sys.exit(1)

    db_url = _get_database_url(env)

    cmd = ["psql"]
    if db_url:
        cmd.append(db_url)
    else:
        cmd.extend(["-h", "localhost", "-U", "postgres", "-d", "lightwave"])

    cmd.extend(["-f", dump_file])

    if verbose:
        print(f"Running: {' '.join(cmd)}")

    try:
        subprocess.run(cmd, check=True)
        if json_output:
            return {"success": True, "message": f"Database restored from {dump_file}"}
        print(f"Database restored from {dump_file}")
        return None
    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)


def db_reset(
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Reset database (drop + migrate).

    SST: domains.db.commands[name=reset]
    flags: [--env, --confirm]
    """
    env = "dev"
    confirm = "--confirm" in args

    for i, arg in enumerate(args):
        if arg == "--env" and i + 1 < len(args):
            env = args[i + 1]

    if dry_run:
        steps = [
            "Drop all tables",
            "Run Django migrations",
            "Create superuser (if configured)",
        ]
        if not json_output:
            print(f"[dry-run] Would reset {env} database:")
            for step in steps:
                print(f"  - {step}")
        return {"success": True, "dry_run": True, "env": env, "steps": steps}

    # Safety checks
    if env == "prod":
        if json_output:
            return {"success": False, "error": "Cannot reset production database"}
        print("Error: Cannot reset production database", file=sys.stderr)
        sys.exit(1)

    if not confirm:
        if json_output:
            return {"success": False, "error": "Use --confirm to reset database"}
        print("Error: Use --confirm flag to reset database", file=sys.stderr)
        sys.exit(1)

    # Find project root
    project_root = _find_project_root()
    manage_py = project_root / "manage.py" if project_root else Path("manage.py")

    if not manage_py.exists():
        # Try lightwave-platform
        manage_py = Path("lightwave-platform/src/backend/manage.py")

    if not manage_py.exists():
        if json_output:
            return {"success": False, "error": "manage.py not found"}
        print("Error: manage.py not found", file=sys.stderr)
        sys.exit(1)

    if verbose:
        print(f"Using manage.py: {manage_py}")

    try:
        # Drop and recreate database via Django
        if verbose:
            print("Running: python manage.py flush --no-input")
        subprocess.run(
            ["python", str(manage_py), "flush", "--no-input"],
            check=True,
            cwd=manage_py.parent,
        )

        # Run migrations
        if verbose:
            print("Running: python manage.py migrate")
        subprocess.run(
            ["python", str(manage_py), "migrate"],
            check=True,
            cwd=manage_py.parent,
        )

        if json_output:
            return {"success": True, "message": f"Database {env} reset successfully"}
        print(f"Database {env} reset successfully")
        return None

    except subprocess.CalledProcessError as e:
        if json_output:
            return {"success": False, "error": str(e)}
        sys.exit(e.returncode)
