"""
Docker and container helper functions for Dazzle CLI.

Contains utilities for Docker container management,
dev infrastructure (Postgres + Redis via Docker Compose),
and production artifact generation (Dockerfile, docker-compose, etc.).
"""

import subprocess
import time
from pathlib import Path


def get_container_name(project_root: Path, project_name: str | None = None) -> str:
    """Get the Docker container name for a project.

    Uses project_name from manifest if provided, otherwise falls back to directory name.
    """
    base_name = project_name or project_root.resolve().name
    return f"dazzle-{base_name}"


def is_container_running(container_name: str) -> bool:
    """Check if a Docker container is running."""
    import subprocess

    try:
        result = subprocess.run(
            ["docker", "ps", "-q", "-f", f"name={container_name}"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        return bool(result.stdout.strip())
    except (subprocess.SubprocessError, FileNotFoundError):
        return False


def generate_production_main(app_name: str, include_frontend: bool) -> str:
    """Generate production-ready main.py entry point."""
    return f'''"""
Production entry point for {app_name}.

Auto-generated by Dazzle Dazzle build.

Usage:
    python main.py                    # Run with defaults
    python main.py --port 8080        # Custom port
    python main.py --host 0.0.0.0     # Bind to all interfaces

Environment variables:
    PORT          - Server port (default: 8000)
    HOST          - Server host (default: 0.0.0.0)
    DATABASE_URL  - PostgreSQL connection URL
    REDIS_URL     - Redis connection URL
    LOG_LEVEL     - Logging level (default: INFO)
"""

import argparse
import logging
import os
from pathlib import Path

# Configure logging
log_level = os.getenv("LOG_LEVEL", "INFO").upper()
logging.basicConfig(
    level=getattr(logging, log_level, logging.INFO),
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)


def main():
    parser = argparse.ArgumentParser(description="{app_name} - Dazzle Production Server")
    parser.add_argument("--port", type=int, default=int(os.getenv("PORT", "8000")))
    parser.add_argument("--host", type=str, default=os.getenv("HOST", "0.0.0.0"))
    parser.add_argument("--reload", action="store_true", help="Enable auto-reload (dev only)")
    args = parser.parse_args()

    # Database setup
    db_url = os.getenv("DATABASE_URL", "")
    if not db_url:
        logger.error("DATABASE_URL environment variable is required")
        return 1

    logger.info(f"Starting {app_name}")
    logger.info(f"  Host: {{args.host}}:{{args.port}}")
    logger.info(f"  Database: {{db_url}}")

    try:
        import uvicorn
        from dazzle_back.runtime import create_app_from_json

        spec_path = Path(__file__).parent / "backend" / "backend-spec.json"
        if not spec_path.exists():
            logger.error(f"Backend spec not found: {{spec_path}}")
            return 1

        app = create_app_from_json(str(spec_path), db_path=db_url)

        uvicorn.run(
            app,
            host=args.host,
            port=args.port,
            reload=args.reload,
            log_level=log_level.lower(),
        )

    except ImportError as e:
        logger.error(f"Missing dependencies: {{e}}")
        logger.error("Install with: pip install -r requirements.txt")
        return 1
    except Exception as e:
        logger.error(f"Failed to start: {{e}}")
        return 1

    return 0


if __name__ == "__main__":
    exit(main())
'''


def generate_requirements() -> str:
    """Generate requirements.txt for production."""
    return """# Runtime Production Dependencies
# Generated by: dazzle build

# Core runtime
dazzle-dsl[serve]
pydantic>=2.0.0

# Database
aiosqlite>=0.19.0
asyncpg>=0.29.0

# Optional: Redis (for Celery workers / caching)
# redis>=5.0.0

# Optional: Production server
gunicorn>=21.0.0

# Optional: Monitoring
prometheus-fastapi-instrumentator>=6.0.0
"""


def generate_dockerfile(app_name: str, include_frontend: bool) -> str:
    """Generate multi-stage Dockerfile."""
    frontend_stage = ""
    copy_frontend = ""
    if include_frontend:
        frontend_stage = """
# Frontend build stage
FROM node:20-alpine AS frontend-builder
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build
"""
        copy_frontend = """
# Copy frontend build
COPY --from=frontend-builder /app/frontend/dist /app/static
ENV STATIC_FILES_DIR=/app/static
"""

    return f"""# Dockerfile for {app_name}
# Generated by: dazzle build
{frontend_stage}
# Backend build stage
FROM python:3.11-slim AS backend

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
{copy_frontend}
# Copy backend
COPY backend/ ./backend/
COPY main.py .

# Environment
ENV PORT=8000
ENV HOST=0.0.0.0
ENV LOG_LEVEL=INFO

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
    CMD curl -f http://localhost:${{PORT}}/health || exit 1

EXPOSE ${{PORT}}

CMD ["python", "main.py"]
"""


def generate_docker_compose(app_name: str) -> str:
    """Generate docker-compose.yml for production deployment."""
    return f"""# Docker Compose for {app_name}
# Generated by: dazzle build

services:
  app:
    build: .
    ports:
      - "${{PORT:-8000}}:8000"
    environment:
      - PORT=8000
      - HOST=0.0.0.0
      - DATABASE_URL=postgresql://{app_name}:{app_name}@postgres:5432/{app_name}
      - REDIS_URL=redis://redis:6379/0
      - LOG_LEVEL=${{LOG_LEVEL:-INFO}}
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 10s

  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: {app_name}
      POSTGRES_USER: {app_name}
      POSTGRES_PASSWORD: {app_name}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U {app_name}"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

volumes:
  pgdata:
    driver: local
"""


def generate_local_compose(app_name: str) -> str:
    """Generate docker-compose.local.yml with Postgres + Redis backing services."""
    return f"""# Local backing services for {app_name}
# Generated by: dazzle build
#
# Start with: docker compose -f docker-compose.local.yml up -d
# Stop with:  docker compose -f docker-compose.local.yml down
# Reset data: docker compose -f docker-compose.local.yml down -v

services:
  postgres:
    image: postgres:16-alpine
    ports:
      - "${{PGPORT:-5433}}:5432"
    environment:
      POSTGRES_DB: {app_name}
      POSTGRES_USER: {app_name}
      POSTGRES_PASSWORD: localdev
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U {app_name}"]
      interval: 5s
      timeout: 3s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "${{REDIS_PORT:-6380}}:6379"
    volumes:
      - redisdata:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  pgdata:
    driver: local
  redisdata:
    driver: local
"""


def generate_local_run_script(app_name: str) -> str:
    """Generate scripts/run_local.sh for full-framework local development."""
    return f"""#!/usr/bin/env bash
# Local development runner for {app_name}
# Generated by: dazzle build
#
# Prerequisites:
#   docker compose -f docker-compose.local.yml up -d
#   pip install -e .   # or: pip install dazzle
#
# Usage:
#   ./scripts/run_local.sh              # Start dev server
#   ./scripts/run_local.sh --reload     # With hot-reload

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"

# Load .env if present
if [ -f "$PROJECT_ROOT/.env" ]; then
    set -a
    source "$PROJECT_ROOT/.env"
    set +a
fi

# Database (Postgres via docker-compose.local.yml)
export DATABASE_URL="${{DATABASE_URL:-postgresql://{app_name}:{app_name}@localhost:${{PGPORT:-5433}}/{app_name}}}"

# Redis (via docker-compose.local.yml)
export REDIS_URL="${{REDIS_URL:-redis://localhost:${{REDIS_PORT:-6380}}/0}}"

# Server settings
export PORT="${{PORT:-8000}}"
export HOST="${{HOST:-0.0.0.0}}"
export LOG_LEVEL="${{LOG_LEVEL:-INFO}}"

# Dazzle project root (for create_app_factory)
export DAZZLE_PROJECT_ROOT="${{DAZZLE_PROJECT_ROOT:-$PROJECT_ROOT}}"

echo "Starting {app_name} (local dev)"
echo "  Database: $DATABASE_URL"
echo "  Redis:    $REDIS_URL"
echo "  Server:   http://$HOST:$PORT"
echo ""

exec uvicorn \\
    "dazzle_back.runtime.server:create_app_factory" \\
    --factory \\
    --host "$HOST" \\
    --port "$PORT" \\
    --log-level "${{LOG_LEVEL,,}}" \\
    "$@"
"""


def generate_env_template(app_name: str) -> str:
    """Generate environment template."""
    return f"""# Environment configuration for {app_name}
# Generated by: dazzle build
#
# Copy this file to .env and customize:
#   cp .env.example .env

# Server configuration
PORT=8000
HOST=0.0.0.0

# Database (Postgres — auto-started by dazzle serve)
DATABASE_URL=postgresql://dazzle:dazzle@localhost:5432/dazzle

# Redis (auto-started by dazzle serve)
REDIS_URL=redis://localhost:6379/0

# Logging
LOG_LEVEL=INFO

# Security (generate your own secret key!)
# SECRET_KEY=your-secret-key-here

# CORS (comma-separated origins)
# CORS_ORIGINS=http://localhost:3000,https://myapp.com

# Optional: Auth settings
# AUTH_ENABLED=true
# JWT_SECRET=your-jwt-secret

# Optional: File uploads
# FILES_ENABLED=true
# FILES_PATH=./uploads
# MAX_FILE_SIZE=10485760

# Optional: Celery workers (requires Redis)
# USE_CELERY_PROCESSES=true

# Production settings
# WORKERS=4
# TIMEOUT=30
"""


# =============================================================================
# Dev Infrastructure (Docker Compose for Postgres + Redis)
# =============================================================================

DEV_COMPOSE_TEMPLATE = """# Dazzle dev infrastructure for {project_name}
# Generated by: dazzle serve
#
# Provides PostgreSQL + Redis for local development.
# The app itself runs on the host via dazzle serve.

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: dazzle
      POSTGRES_USER: dazzle
      POSTGRES_PASSWORD: dazzle
    ports:
      - "{pg_port}:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dazzle"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "{redis_port}:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s

volumes:
  pgdata:
"""


def generate_dev_compose(
    project_name: str,
    pg_port: int = 5432,
    redis_port: int = 6379,
) -> str:
    """Generate docker-compose.yml for dev infrastructure (Postgres + Redis).

    This compose file provides backing services only. The Dazzle app
    runs on the host and connects to these services.

    Args:
        project_name: Project name (used in comments).
        pg_port: Host port for PostgreSQL (default 5432).
        redis_port: Host port for Redis (default 6379).

    Returns:
        docker-compose.yml content string.
    """
    return DEV_COMPOSE_TEMPLATE.format(
        project_name=project_name,
        pg_port=pg_port,
        redis_port=redis_port,
    )


def dev_compose_path(project_root: Path) -> Path:
    """Return the path to the dev infrastructure compose file."""
    return project_root / ".dazzle" / "docker-compose.yml"


def write_dev_compose(
    project_root: Path,
    project_name: str,
    pg_port: int = 5432,
    redis_port: int = 6379,
) -> Path:
    """Write the dev infrastructure compose file to .dazzle/.

    Args:
        project_root: Project root directory.
        project_name: Project name.
        pg_port: Host port for PostgreSQL.
        redis_port: Host port for Redis.

    Returns:
        Path to the written compose file.
    """
    compose_file = dev_compose_path(project_root)
    compose_file.parent.mkdir(parents=True, exist_ok=True)
    compose_file.write_text(
        generate_dev_compose(project_name, pg_port=pg_port, redis_port=redis_port)
    )
    return compose_file


def dev_database_url(pg_port: int = 5432) -> str:
    """Return the DATABASE_URL for the dev infrastructure Postgres."""
    return f"postgresql://dazzle:dazzle@localhost:{pg_port}/dazzle"


def dev_redis_url(redis_port: int = 6379) -> str:
    """Return the REDIS_URL for the dev infrastructure Redis."""
    return f"redis://localhost:{redis_port}/0"


def start_dev_infrastructure(
    project_root: Path,
    project_name: str,
    pg_port: int = 5432,
    redis_port: int = 6379,
    timeout: int = 60,
) -> tuple[str, str]:
    """Start dev infrastructure (Postgres + Redis) via Docker Compose.

    Writes the compose file, runs ``docker compose up -d``, and waits
    for healthchecks to pass.

    Args:
        project_root: Project root directory.
        project_name: Project name.
        pg_port: Host port for PostgreSQL.
        redis_port: Host port for Redis.
        timeout: Max seconds to wait for services to become healthy.

    Returns:
        (database_url, redis_url) tuple.

    Raises:
        RuntimeError: If Docker is not available or services fail to start.
    """
    compose_file = write_dev_compose(
        project_root, project_name, pg_port=pg_port, redis_port=redis_port
    )

    # Start services
    try:
        result = subprocess.run(
            ["docker", "compose", "-f", str(compose_file), "up", "-d"],
            capture_output=True,
            text=True,
            timeout=120,
        )
    except FileNotFoundError:
        raise RuntimeError("Docker is not installed. Install Docker or use: dazzle serve --local")
    except subprocess.TimeoutExpired:
        raise RuntimeError("Docker compose up timed out")

    if result.returncode != 0:
        raise RuntimeError(f"Docker compose up failed:\n{result.stderr}")

    # Wait for services to be healthy
    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        try:
            health = subprocess.run(
                [
                    "docker",
                    "compose",
                    "-f",
                    str(compose_file),
                    "ps",
                    "--format",
                    "{{.Health}}",
                ],
                capture_output=True,
                text=True,
                timeout=10,
            )
            lines = [ln.strip() for ln in health.stdout.strip().splitlines() if ln.strip()]
            if lines and all(ln == "healthy" for ln in lines):
                break
        except (subprocess.SubprocessError, subprocess.TimeoutExpired):
            pass
        time.sleep(2)
    else:
        raise RuntimeError(
            f"Dev infrastructure services did not become healthy within {timeout}s. "
            f"Check: docker compose -f {compose_file} ps"
        )

    return dev_database_url(pg_port), dev_redis_url(redis_port)


def stop_dev_infrastructure(project_root: Path) -> bool:
    """Stop dev infrastructure if running.

    Args:
        project_root: Project root directory.

    Returns:
        True if stopped successfully (or compose file didn't exist).
    """
    compose_file = dev_compose_path(project_root)
    if not compose_file.exists():
        return True

    try:
        result = subprocess.run(
            ["docker", "compose", "-f", str(compose_file), "down"],
            capture_output=True,
            text=True,
            timeout=60,
        )
        return result.returncode == 0
    except (subprocess.SubprocessError, FileNotFoundError):
        return False
