"""Implementation of the `gentem fastapi` command."""

from datetime import datetime
from pathlib import Path
from typing import Optional

from rich import print
from rich.panel import Panel

from gentem.utils.validators import (
    ValidationError,
    validate_db_type,
    validate_project_name,
)


def generate_slug(name: str) -> str:
    """Generate a URL-safe slug from the project name."""
    return name.lower().replace("_", "-")


def generate_class_name(name: str) -> str:
    """Generate a Python class name from the project name."""
    return "".join(word.capitalize() for word in name.split("_"))


def generate_context(
    project_name: str,
    async_mode: bool,
    db_type: Optional[str],
    author: str,
    description: str,
) -> dict:
    """Generate the template context for a FastAPI project.

    Args:
        project_name: Name of the project.
        async_mode: Whether to use async mode.
        db_type: Database type (asyncpg, etc.).
        author: Author name.
        description: Project description.

    Returns:
        Dictionary of template variables.
    """
    now = datetime.now()
    slug = generate_slug(project_name)

    return {
        "project_name": project_name,
        "project_slug": slug,
        "class_name": generate_class_name(project_name),
        "author": author or "Gentem User",
        "email": f"user@{slug}.dev",
        "description": description or f"A FastAPI project generated by Gentem.",
        "version": "0.1.0",
        "python_version": "3.10",
        "python_versions": ["3.10", "3.11", "3.12"],
        "async_mode": async_mode,
        "db_type": db_type,
        "has_database": db_type is not None,
        "year": now.year,
        "month": now.strftime("%B"),
    }


def create_fastapi_project(
    project_name: str,
    async_mode: bool = False,
    db_type: str = "",
    author: str = "",
    description: str = "",
    dry_run: bool = False,
    verbose: bool = False,
) -> None:
    """Create a new FastAPI project.

    Args:
        project_name: Name of the project to create.
        async_mode: Use async mode with lifespan.
        db_type: Database type (asyncpg for async SQLAlchemy).
        author: Author name.
        description: Project description.
        dry_run: Preview without creating files.
        verbose: Show verbose output.
    """
    # Validate inputs
    try:
        project_name = validate_project_name(project_name)
        db_type = validate_db_type(db_type)
    except ValidationError as e:
        print(f"[red]Error: {e}[/]")
        raise SystemExit(1)

    # Generate template context
    context = generate_context(
        project_name=project_name,
        async_mode=async_mode,
        db_type=db_type,
        author=author,
        description=description,
    )

    # Determine output path
    output_path = Path.cwd() / project_name

    if verbose:
        print(f"Output path: {output_path}")
        print(f"Async mode: {async_mode}")
        print(f"Database type: {db_type or 'none'}")

    # Show summary
    db_info = f"Database: {db_type}" if db_type else "No database"
    print(Panel(
        f"[bold]Creating new FastAPI project:[/] [cyan]{project_name}[/]\n"
        f"[dim]Async:[/] {async_mode} | [dim]{db_info}[/]\n"
        f"[dim]Author:[/] {context['author']}",
        title="Gentem",
        expand=False,
    ))

    if dry_run:
        print("[yellow]DRY RUN - No files will be created[/]")
        print(f"\nProject would be created at: {output_path}")
        print("\nFiles that would be created:")
        files = get_fastapi_project_files(project_name, db_type is not None)
        for item in files:
            print(f"  - {item}")
        return

    # Create project files
    try:
        create_fastapi_project_files(
            project_name=project_name,
            context=context,
            output_path=output_path,
            async_mode=async_mode,
            db_type=db_type,
        )
        print(f"\n[green]✓ FastAPI project '{project_name}' created successfully![/]")
        print(f"\nNext steps:")
        print(f"  cd {project_name}")
        print(f"  pip install -r requirements.txt")
        print(f"  uvicorn {context['project_slug']}.main:app --reload")
    except Exception as e:
        print(f"[red]Error creating project: {e}[/]")
        raise SystemExit(1)


def get_fastapi_project_files(project_name: str, has_database: bool) -> list[str]:
    """Get list of files that would be created for a FastAPI project.

    Args:
        project_name: Name of the project.
        has_database: Whether database support is included.

    Returns:
        List of file paths.
    """
    files = [
        f"{project_name}/",
        f"{project_name}/requirements.txt",
        f"{project_name}/.env",
        f"{project_name}/.gitignore",
        f"{project_name}/README.md",
        f"{project_name}/app/",
        f"{project_name}/app/__init__.py",
        f"{project_name}/app/main.py",
        f"{project_name}/app/core/",
        f"{project_name}/app/core/__init__.py",
        f"{project_name}/app/core/config.py",
        f"{project_name}/app/core/exceptions.py",
        f"{project_name}/app/deps/",
        f"{project_name}/app/deps/__init__.py",
        f"{project_name}/app/utils/",
        f"{project_name}/app/utils/__init__.py",
        f"{project_name}/app/v1/",
        f"{project_name}/app/v1/__init__.py",
        f"{project_name}/app/v1/apis/",
        f"{project_name}/app/v1/apis/__init__.py",
        f"{project_name}/app/v1/apis/routes.py",
        f"{project_name}/app/services/",
        f"{project_name}/app/services/__init__.py",
        f"{project_name}/app/schemas/",
        f"{project_name}/app/schemas/__init__.py",
    ]

    if has_database:
        files.extend([
            f"{project_name}/app/models/",
            f"{project_name}/app/models/__init__.py",
            f"{project_name}/app/core/database.py",
        ])

    return files


def create_fastapi_project_files(
    project_name: str,
    context: dict,
    output_path: Path,
    async_mode: bool,
    db_type: Optional[str],
) -> None:
    """Create all FastAPI project files.

    Args:
        project_name: Name of the project.
        context: Template context.
        output_path: Path to create the project at.
        async_mode: Whether to use async mode.
        db_type: Database type.
    """
    slug = context["project_slug"]

    # Create directories
    output_path.mkdir(parents=True, exist_ok=True)

    # Create requirements.txt
    requirements = [
        "fastapi>=0.104.0",
        "uvicorn[standard]>=0.24.0",
        "pydantic>=2.5.0",
        "pydantic-settings>=2.0.0",
        "python-multipart>=0.0.6",
    ]

    if db_type == "asyncpg":
        requirements.extend([
            "sqlalchemy[asyncio]>=2.0.0",
            "asyncpg>=0.29.0",
        ])

    requirements_content = "\n".join(sorted(requirements))

    # Create .env
    env_content = f"""# {project_name} Environment Variables

# Application
APP_NAME={project_name}
DEBUG=true
API_V1_STR=/api/v1

# Database
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/{slug}
"""

    # Create .gitignore
    gitignore_content = """# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
Pipfile.lock

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# IDEs
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# Logs
logs/
*.log
"""

    # Create README.md
    async_note = "Async with lifespan" if async_mode else "Sync with lifespan"
    db_note = "Database support with asyncpg + SQLAlchemy" if db_type == "asyncpg" else "No database"
    models_note = "(with --db asyncpg)" if db_type == "asyncpg" else ""

    readme_content = f"""# {project_name}

{context["description"]}

## Features

- FastAPI REST API
- {async_note}
- {db_note}
- Project structure with core, deps, utils, v1/apis, services, schemas
- Exception handlers
- Middleware setup
- Health check endpoint

## Installation

```bash
pip install -r requirements.txt
```

## Running

```bash
uvicorn {slug}.main:app --reload
```

## API Documentation

Once running, access:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- OpenAPI: http://localhost:8000/openapi.json

## Project Structure

```
app/
├── __init__.py
├── main.py              # FastAPI application
├── core/
│   ├── __init__.py
│   ├── config.py        # Settings
│   └── exceptions.py    # Custom exceptions
├── deps/
│   └── __init__.py      # Dependencies
├── utils/
│   └── __init__.py      # Utility functions
├── v1/
│   ├── __init__.py
│   └── apis/
│       ├── __init__.py
│       └── routes.py    # API routes
├── services/
│   └── __init__.py      # Business logic
├── schemas/
│   └── __init__.py      # Pydantic schemas
└── models/
    └── __init__.py      # SQLAlchemy models {models_note}
```

## License

This project is licensed under the MIT License.
"""

    # Write common files
    (output_path / "requirements.txt").write_text(requirements_content, encoding="utf-8")
    (output_path / ".env").write_text(env_content, encoding="utf-8")
    (output_path / ".gitignore").write_text(gitignore_content, encoding="utf-8")
    (output_path / "README.md").write_text(readme_content, encoding="utf-8")

    # Create app directory structure
    app_dir = output_path / "app"
    app_dir.mkdir(parents=True, exist_ok=True)
    (app_dir / "__init__.py").write_text(f'""" {project_name} - FastAPI application."""\n', encoding="utf-8")

    # Create core module
    core_dir = app_dir / "core"
    core_dir.mkdir(parents=True, exist_ok=True)
    (core_dir / "__init__.py").write_text('"""Core module: config, exceptions, etc."""\n', encoding="utf-8")

    # Create config.py
    config_py = f'''"""Application configuration."""

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Application settings."""

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )

    app_name: str = Field(default="{project_name}", description="Application name")
    debug: bool = Field(default=False, description="Debug mode")
    api_v1_str: str = Field(default="/api/v1", description="API version 1 prefix")
    database_url: str = Field(default="", description="Database connection URL")


settings = Settings()
'''
    (core_dir / "config.py").write_text(config_py, encoding="utf-8")

    # Create exceptions.py
    exceptions_py = '''"""Custom exceptions and exception handlers."""

from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse
from typing import Any, Dict


class AppException(Exception):
    """Base application exception."""

    def __init__(self, message: str, status_code: int = 500, details: Dict[str, Any] = None):
        self.message = message
        self.status_code = status_code
        self.details = details or {}


class NotFoundException(AppException):
    """Resource not found."""

    def __init__(self, resource: str = "Resource"):
        super().__init__(
            message=f"{resource} not found",
            status_code=404,
            details={"resource": resource},
        )


class ValidationException(AppException):
    """Validation error."""

    def __init__(self, message: str, details: Dict[str, Any] = None):
        super().__init__(
            message=message,
            status_code=422,
            details=details,
        )


async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
    """Handle AppException."""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": {
                "message": exc.message,
                "details": exc.details,
            }
        },
    )


async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
    """Handle HTTPException."""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": {
                "message": str(exc.detail) if exc.detail else "HTTP Error",
            }
        },
    )


async def validation_exception_handler(request: Request, exc) -> JSONResponse:
    """Handle validation errors from Pydantic."""
    return JSONResponse(
        status_code=422,
        content={
            "error": {
                "message": "Validation error",
                "details": exc.errors() if hasattr(exc, "errors") else str(exc),
            }
        },
    )


def setup_exception_handlers(app) -> None:
    """Setup exception handlers for the FastAPI app."""
    app.add_exception_handler(AppException, app_exception_handler)
    app.add_exception_handler(HTTPException, http_exception_handler)
    app.add_exception_handler(ValidationException, validation_exception_handler)
'''
    (core_dir / "exceptions.py").write_text(exceptions_py, encoding="utf-8")

    # Create deps module
    deps_dir = app_dir / "deps"
    deps_dir.mkdir(parents=True, exist_ok=True)
    (deps_dir / "__init__.py").write_text('"""Dependencies."""\n', encoding="utf-8")

    # Create utils module
    utils_dir = app_dir / "utils"
    utils_dir.mkdir(parents=True, exist_ok=True)
    (utils_dir / "__init__.py").write_text('"""Utility functions."""\n', encoding="utf-8")

    # Create v1 module
    v1_dir = app_dir / "v1"
    v1_dir.mkdir(parents=True, exist_ok=True)
    (v1_dir / "__init__.py").write_text('"""API version 1."""\n', encoding="utf-8")

    # Create v1/apis module
    apis_dir = v1_dir / "apis"
    apis_dir.mkdir(parents=True, exist_ok=True)
    (apis_dir / "__init__.py").write_text('"""API routes."""\n', encoding="utf-8")

    # Create routes.py
    routes_py = f'''"""API routes."""

from fastapi import APIRouter
from typing import Any, Dict

router = APIRouter()


@router.get("/healthz", tags=["Health"])
async def health_check() -> Dict[str, str]:
    """Health check endpoint."""
    return {{"status": "healthy"}}


@router.get("/", tags=["Root"])
async def root() -> Dict[str, Any]:
    """Root endpoint."""
    return {{
        "app": "{project_name}",
        "version": "{context["version"]}",
        "docs": "/docs",
    }}
'''
    (apis_dir / "routes.py").write_text(routes_py, encoding="utf-8")

    # Create services module
    services_dir = app_dir / "services"
    services_dir.mkdir(parents=True, exist_ok=True)
    (services_dir / "__init__.py").write_text('"""Business logic services."""\n', encoding="utf-8")

    # Create schemas module
    schemas_dir = app_dir / "schemas"
    schemas_dir.mkdir(parents=True, exist_ok=True)
    (schemas_dir / "__init__.py").write_text('"""Pydantic schemas."""\n', encoding="utf-8")

    # Create database module if db_type is set
    if db_type == "asyncpg":
        database_py = f'''"""Database configuration and session management."""

from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy.orm import DeclarativeBase

from app.core.config import settings

# Create async engine
engine = create_async_engine(
    settings.database_url,
    echo=settings.debug,
    pool_pre_ping=True,
)

# Create async session factory
async_session_factory = async_sessionmaker(
    engine,
    class_=AsyncSession,
    expire_on_commit=False,
)


class Base(DeclarativeBase):
    """Base class for SQLAlchemy models."""
    pass


async def get_db() -> AsyncSession:
    """Dependency that provides a database session."""
    async with async_session_factory() as session:
        try:
            yield session
        finally:
            await session.close()


async def init_db() -> None:
    """Initialize database tables."""
    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)


async def close_db() -> None:
    """Close database connections."""
    await engine.dispose()
'''
        (core_dir / "database.py").write_text(database_py, encoding="utf-8")

        # Create models module
        models_dir = app_dir / "models"
        models_dir.mkdir(parents=True, exist_ok=True)
        (models_dir / "__init__.py").write_text('''"""SQLAlchemy models."""
from app.core.database import Base

# Import models here
# Example:
# class User(Base):
#     __tablename__ = "users"
#     id = Column(Integer, primary_key=True, index=True)
#     email = Column(String, unique=True, index=True)
''', encoding="utf-8")
    else:
        # Create empty models __init__.py
        models_dir = app_dir / "models"
        models_dir.mkdir(parents=True, exist_ok=True)
        (models_dir / "__init__.py").write_text('''"""SQLAlchemy models."""
# Add models here
# Example:
# from app.core.database import Base
# from sqlalchemy import Column, Integer, String
#
# class User(Base):
#     __tablename__ = "users"
#     id = Column(Integer, primary_key=True, index=True)
''', encoding="utf-8")

    # Create main.py
    if async_mode:
        lifespan_section = '''
from contextlib import asynccontextmanager

from app.core.database import init_db, close_db


@asynccontextmanager
async def lifespan(app):
    """Lifespan context manager for startup and shutdown events."""
    # Startup
    print("Starting up...")
    await init_db()
    yield
    # Shutdown
    print("Shutting down...")
    await close_db()
'''
        lifespan_arg = "lifespan=lifespan"
    else:
        lifespan_section = '''
# No lifespan - simple startup
'''
        lifespan_arg = ""

    main_py = f'''"""FastAPI application entry point."""

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.core.config import settings
from app.core.exceptions import setup_exception_handlers
from app.v1.apis.routes import router as v1_router
{lifespan_section}


def create_app() -> FastAPI:
    """Create and configure the FastAPI application."""
    app = FastAPI(
        title=settings.app_name,
        description="{context["description"]}",
        version="{context["version"]}",
        docs_url="/docs",
        redoc_url="/redoc",
        openapi_url="/openapi.json",
        {lifespan_arg}
    )

    # CORS middleware
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    # Setup exception handlers
    setup_exception_handlers(app)

    # Include API routers
    app.include_router(v1_router, prefix=settings.api_v1_str)

    return app


app = create_app()


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(
        "app.main:app",
        host="0.0.0.0",
        port=8000,
        reload=settings.debug,
    )
'''
    (app_dir / "main.py").write_text(main_py, encoding="utf-8")
