import re
from pathlib import Path

from fastapi import APIRouter, HTTPException

from mosaygent.command_runner import run_command
from mosaygent.logger import get_logger

logger = get_logger(__name__)


def parse_publishable_key_from_output(output: str) -> str | None:
    """Extract the publishable key from supabase start/status output."""
    for line in output.strip().split('\n'):
        if 'publishable key' in line.lower():
            parts = line.split(':', 1)
            if len(parts) == 2:
                return parts[1].strip()
    return None


def update_env_local_with_anon_key(project_path: str, anon_key: str) -> None:
    """Update the .env.local file with the Supabase anon key."""
    env_file = Path(project_path) / ".env.local"

    if not env_file.exists():
        logger.debug(f".env.local file not found at {env_file}, skipping update")
        return

    content = env_file.read_text()
    updated_content = re.sub(
        r'(EXPO_PUBLIC_SUPABASE_PUB_KEY=).*',
        f'\\1{anon_key}',
        content
    )

    env_file.write_text(updated_content)
    logger.info(f"Updated EXPO_PUBLIC_SUPABASE_PUB_KEY in {env_file}")

router = APIRouter(
    tags=['Supabase Routes'],
)


@router.get("/supabase/check-installation")
async def check_supabase_installation() -> dict:
    """Check if Docker and Supabase CLI are installed."""
    logger.info("Checking Docker and Supabase CLI installation")

    docker_result = await run_command(['docker', '--version'], timeout=10, raise_on_error=False)

    if not docker_result.success:
        logger.warning("Docker is not installed")
        return {
            "docker_installed": False,
            "message": "Docker is not installed. Please install Docker to use Supabase."
        }

    logger.info("Docker is installed")
    docker_version = docker_result.stdout.strip()
    logger.debug(f"Docker version: {docker_version}")

    supabase_result = await run_command(['supabase', '--version'], timeout=10, raise_on_error=False)

    if not supabase_result.success:
        logger.warning("Supabase CLI is not installed")
        return {
            "docker_installed": True,
            "docker_version": docker_version,
            "supabase_installed": False,
            "message": "Supabase CLI is not installed. Please install the Supabase CLI."
        }

    logger.info("Supabase CLI is installed")
    supabase_version = supabase_result.stdout.strip()
    logger.debug(f"Supabase CLI version: {supabase_version}")

    return {
        "docker_installed": True,
        "docker_version": docker_version,
        "supabase_installed": True,
        "supabase_version": supabase_version
    }


@router.get("/supabase/migrations")
async def list_migrations(project_path: str) -> dict:
    """List all migration files in the supabase/migrations directory."""
    logger.info(f"Listing migrations for project: {project_path}")

    migrations_dir = Path(project_path) / "supabase" / "migrations"
    logger.debug(f"Looking for migrations in: {migrations_dir}")

    if not migrations_dir.exists():
        logger.warning(f"Migrations directory does not exist: {migrations_dir}")
        raise HTTPException(
            status_code=404,
            detail=f"Migrations directory not found at {migrations_dir}"
        )

    if not migrations_dir.is_dir():
        logger.error(f"Path exists but is not a directory: {migrations_dir}")
        raise HTTPException(
            status_code=400,
            detail=f"Path {migrations_dir} is not a directory"
        )

    migration_files = sorted([f.name for f in migrations_dir.iterdir() if f.is_file()])
    logger.info(f"Found {len(migration_files)} migration files")
    logger.debug(f"Migration files: {migration_files}")

    return {
        "migrations": migration_files,
        "count": len(migration_files),
        "path": str(migrations_dir)
    }


@router.post("/supabase/migrations")
async def create_migration(project_path: str) -> dict:
    """Create a new migration by diffing the local database schema."""
    logger.info(f"Creating migration for project: {project_path}")

    migrations_dir = Path(project_path) / "supabase" / "migrations"
    if not migrations_dir.exists():
        logger.warning(f"Migrations directory does not exist: {migrations_dir}")
        raise HTTPException(
            status_code=404,
            detail=f"Migrations directory not found at {migrations_dir}"
        )

    existing_files = set(f.name for f in migrations_dir.iterdir() if f.is_file())
    logger.debug(f"Existing migrations before diff: {existing_files}")

    result = await run_command(
        ['supabase', 'db', 'diff', '-f', 'mosayic_migration'],
        timeout=60,
        cwd=project_path,
        raise_on_error=False
    )

    if not result.success:
        logger.error(f"Migration diff failed: {result.stderr}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to create migration: {result.stderr}"
        )

    logger.info("Migration diff completed successfully")

    current_files = set(f.name for f in migrations_dir.iterdir() if f.is_file())
    new_files = current_files - existing_files
    logger.info(f"New migration files created: {new_files}")

    migration_list = []
    for f in sorted(current_files):
        if f in new_files:
            migration_list.append(f"__{f}")
        else:
            migration_list.append(f)

    return {
        "migrations": migration_list,
        "count": len(migration_list),
        "new_migration": list(new_files)[0] if new_files else None,
        "path": str(migrations_dir)
    }


@router.post("/supabase/reset-database")
async def reset_database(project_path: str) -> dict:
    """Reset the local Supabase database by running supabase db reset."""
    logger.info(f"Resetting local database for project: {project_path}")

    supabase_dir = Path(project_path) / "supabase"
    if not supabase_dir.exists():
        logger.warning(f"Supabase directory does not exist: {supabase_dir}")
        raise HTTPException(
            status_code=404,
            detail=f"Supabase directory not found at {supabase_dir}"
        )

    result = await run_command(
        ['supabase', 'db', 'reset'],
        timeout=120,
        cwd=project_path,
        raise_on_error=False
    )

    if not result.success:
        logger.error(f"Database reset failed: {result.stderr}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to reset database: {result.stderr}"
        )

    logger.info("Database reset completed successfully")

    return {
        "success": True,
        "message": "Local database has been reset successfully"
    }


@router.post("/supabase/start")
async def start_supabase(project_path: str) -> dict:
    """Start the local Supabase server."""
    logger.info(f"Starting Supabase for project: {project_path}")

    supabase_dir = Path(project_path) / "supabase"
    if not supabase_dir.exists():
        logger.warning(f"Supabase directory does not exist: {supabase_dir}")
        raise HTTPException(
            status_code=404,
            detail=f"Supabase directory not found at {supabase_dir}"
        )

    result = await run_command(
        ['supabase', 'start'],
        timeout=300,
        cwd=project_path,
        raise_on_error=False
    )

    if result.success:
        logger.info("Supabase started successfully")
        publishable_key = parse_publishable_key_from_output(result.stdout)
        if publishable_key:
            update_env_local_with_anon_key(project_path, publishable_key)
        else:
            logger.warning("Could not parse publishable key from supabase start output")

        return {
            "message": "Supabase started successfully",
            "output": result.stdout.strip()
        }

    error_output = result.stderr + result.stdout
    logger.warning(f"Supabase start failed: {error_output}")

    project_id_match = re.search(r'supabase stop --project-id\s+(\S+)', error_output)
    if not project_id_match:
        logger.error("Failed to start Supabase and no conflicting project found")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to start Supabase: {error_output}"
        )

    conflicting_project_id = project_id_match.group(1)
    logger.info(f"Found conflicting Supabase instance: {conflicting_project_id}")

    stop_result = await run_command(
        ['supabase', 'stop', '--project-id', conflicting_project_id],
        timeout=60,
        raise_on_error=False
    )

    if not stop_result.success:
        logger.error(f"Failed to stop conflicting instance: {stop_result.stderr}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to stop conflicting Supabase instance {conflicting_project_id}: {stop_result.stderr}"
        )

    logger.info(f"Stopped conflicting instance: {conflicting_project_id}")

    retry_result = await run_command(
        ['supabase', 'start'],
        timeout=300,
        cwd=project_path,
        raise_on_error=False
    )

    if not retry_result.success:
        logger.error(f"Supabase start retry failed: {retry_result.stderr}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to start Supabase after stopping conflicting instance: {retry_result.stderr}"
        )

    logger.info("Supabase started successfully after stopping conflicting instance")
    publishable_key = parse_publishable_key_from_output(retry_result.stdout)
    if publishable_key:
        update_env_local_with_anon_key(project_path, publishable_key)
    else:
        logger.warning("Could not parse publishable key from supabase start output")

    return {
        "message": "Supabase started successfully",
        "stopped_project_id": conflicting_project_id,
        "output": retry_result.stdout.strip()
    }


@router.post("/supabase/stop")
async def stop_supabase(project_path: str) -> dict:
    """Stop the local Supabase server."""
    logger.info(f"Stopping Supabase for project: {project_path}")

    supabase_dir = Path(project_path) / "supabase"
    if not supabase_dir.exists():
        logger.warning(f"Supabase directory does not exist: {supabase_dir}")
        raise HTTPException(
            status_code=404,
            detail=f"Supabase directory not found at {supabase_dir}"
        )

    result = await run_command(
        ['supabase', 'stop'],
        timeout=60,
        cwd=project_path,
        raise_on_error=False
    )

    if not result.success:
        logger.error(f"Supabase stop failed: {result.stderr}")
        raise HTTPException(
            status_code=500,
            detail=f"Failed to stop Supabase: {result.stderr}"
        )

    logger.info("Supabase stopped successfully")
    return {
        "message": "Supabase stopped successfully"
    }


@router.get("/supabase/status")
async def get_supabase_status(project_path: str) -> dict:
    """Get the status of the local Supabase server."""
    logger.info(f"Checking Supabase status for project: {project_path}")

    supabase_dir = Path(project_path) / "supabase"
    if not supabase_dir.exists():
        logger.warning(f"Supabase directory does not exist: {supabase_dir}")
        raise HTTPException(
            status_code=404,
            detail=f"Supabase directory not found at {supabase_dir}"
        )

    result = await run_command(
        ['supabase', 'status'],
        timeout=30,
        cwd=project_path,
        raise_on_error=False
    )

    if not result.success:
        logger.info("Supabase is not running")
        return {
            "running": False,
            "message": "Supabase is not running"
        }

    logger.info("Supabase is running")
    logger.debug(f"Supabase status output: {result.stdout}")

    services = {}
    for line in result.stdout.strip().split('\n'):
        if ':' in line:
            key, value = line.split(':', 1)
            services[key.strip().lower().replace(' ', '_')] = value.strip()

    return {
        "running": True,
        "services": services
    }
