"""caprail new <project-name> — scaffold a new Caprail project."""
from __future__ import annotations

import shutil
import subprocess
import sys
from pathlib import Path

import typer
from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn

console = Console()


def command(
    name: str = typer.Argument(..., help="Project name / directory (use '.' for current directory)"),
    force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing directory"),
) -> None:
    """Create a new Caprail project."""
    if name == ".":
        dest = Path.cwd()
        project_name = dest.name
    else:
        dest = Path.cwd() / name
        project_name = name

    if name != "." and dest.exists():
        if not force:
            console.print(f"[red]Directory already exists:[/] {dest}")
            raise typer.Exit(1)
        shutil.rmtree(dest)

    if name == "." and not force:
        existing = [p for p in dest.iterdir() if not p.name.startswith(".")]
        if existing:
            console.print(f"[red]Current directory is not empty.[/] Use [cyan]--force[/] to scaffold anyway.")
            raise typer.Exit(1)

    console.print(f"\n[bold green]Creating Caprail project:[/] [cyan]{project_name}[/]\n")

    with Progress(SpinnerColumn(), TextColumn("{task.description}"), console=console) as progress:
        task = progress.add_task("Scaffolding project structure...", total=None)
        _scaffold(dest, project_name)
        progress.update(task, description="[green]Project structure created")

    console.print(f"\n[bold green]✓ Project created![/] [cyan]{dest}[/]\n")
    console.print("  Next steps:")
    if name != ".":
        console.print(f"    [cyan]cd {project_name}[/]")
    console.print("    [cyan]uv sync[/]")
    console.print("    [cyan]npm install[/]")
    console.print("    [cyan]cp .env.example .env[/]")
    console.print("    [cyan]caprail key:generate[/]")
    console.print("    [cyan]caprail serve[/]\n")


def _scaffold(dest: Path, name: str) -> None:
    dest.mkdir(parents=True, exist_ok=True)

    # ── Directory structure ───────────────────────────────────────────
    dirs = [
        "app/Http/Controllers",
        "app/Http/Middleware",
        "app/Http/Requests",
        "app/Models",
        "app/Providers",
        "bootstrap",
        "config",
        "database/migrations",
        "database/seeders",
        "public",
        "resources/css",
        "resources/js",
        "resources/views/layouts",
        "resources/views/auth",
        "routes",
        "storage/app",
        "storage/logs",
        "tests",
    ]
    for d in dirs:
        (dest / d).mkdir(parents=True, exist_ok=True)
        (dest / d / ".gitkeep").touch()

    # ── Files ─────────────────────────────────────────────────────────
    _write(dest / ".env.example", _env_example(name))
    _write(dest / ".gitignore", _gitignore())
    _write(dest / "pyproject.toml", _pyproject(name))
    _write(dest / "README.md", f"# {name}\n\nA Caprail application.\n")

    # Bootstrap
    _write(dest / "bootstrap" / "app.py", _bootstrap_app())

    # Routes
    _write(dest / "routes" / "web.py", _routes_web())
    _write(dest / "routes" / "api.py", _routes_api())

    # Config
    _write(dest / "config" / "app.py", _config_app(name))
    _write(dest / "config" / "database.py", _config_database())
    _write(dest / "config" / "session.py", _config_session())

    # Default controller
    _write(dest / "app" / "Http" / "Controllers" / "HomeController.py", _home_controller())

    # Providers
    _write(dest / "app" / "Providers" / "AppServiceProvider.py", _app_service_provider())
    _write(dest / "app" / "Providers" / "RouteServiceProvider.py", _route_service_provider())

    # Views
    _write(dest / "resources" / "views" / "layouts" / "app.blade.html", _layout_app())
    _write(dest / "resources" / "views" / "welcome.blade.html", _welcome_view())
    _write(dest / "resources" / "views" / "auth" / "login.blade.html", _login_view())

    # Frontend
    _write(dest / "resources" / "css" / "app.css", _app_css())
    _write(dest / "resources" / "js" / "app.js", _app_js())
    _write(dest / "vite.config.js", _vite_config())
    _write(dest / "tailwind.config.js", _tailwind_config())
    _write(dest / "package.json", _package_json(name))

    # Tests
    _write(dest / "tests" / "__init__.py", "")
    _write(dest / "tests" / "test_example.py", _test_example())

    # Public
    _write(dest / "public" / "index.php", "# Caprail serves via ASGI — this file is a placeholder.\n")

    # __init__ files
    for pkg in [
        "app", "app/Http", "app/Http/Controllers", "app/Http/Middleware",
        "app/Http/Requests", "app/Models", "app/Providers", "bootstrap",
        "config", "routes",
    ]:
        p = dest / pkg / "__init__.py"
        if not p.exists():
            p.write_text("")


def _write(path: Path, content: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(content, encoding="utf-8")


# ── Template strings ───────────────────────────────────────────────────

def _env_example(name: str) -> str:
    slug = name.upper().replace("-", "_")
    return f"""APP_NAME={name}
APP_ENV=local
APP_DEBUG=true
APP_KEY=
APP_URL=http://localhost:8000

DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite
# DB_HOST=127.0.0.1
# DB_PORT=5432
# DB_DATABASE={slug.lower()}
# DB_USERNAME=root
# DB_PASSWORD=

SESSION_DRIVER=cookie
SESSION_LIFETIME=120

VITE_APP_NAME=${{APP_NAME}}
"""


def _gitignore() -> str:
    return """.env
__pycache__/
*.pyc
.venv/
venv/
dist/
build/
*.egg-info/
.pytest_cache/
.coverage
htmlcov/
node_modules/
public/build/
storage/logs/*.log
"""


def _pyproject(name: str) -> str:
    return f"""[project]
name = "{name}"
version = "0.1.0"
description = "A Caprail application"
requires-python = ">=3.14"
dependencies = [
    "caprail>=0.1.0",
]

[dependency-groups]
dev = [
    "pytest>=8.3",
    "pytest-asyncio>=0.25",
]

[tool.pytest.ini_options]
asyncio_mode = "auto"
"""


def _bootstrap_app() -> str:
    return '''"""
Bootstrap the Caprail application.
"""
from pathlib import Path

from caprail import Application
from app.Providers.AppServiceProvider import AppServiceProvider
from app.Providers.RouteServiceProvider import RouteServiceProvider

app = Application(base_path=Path(__file__).parent.parent)

app.register_providers([
    AppServiceProvider,
    RouteServiceProvider,
])

# The ASGI callable used by uvicorn / caprail serve
application = app.make_asgi()
'''


def _routes_web() -> str:
    return '''"""
Web routes.

These routes are loaded by the RouteServiceProvider.
"""
from caprail.routing.router import Router
from app.Http.Controllers.HomeController import HomeController


def register(router: Router) -> None:
    router.get("/", HomeController().index).name_as("home")
'''


def _routes_api() -> str:
    return '''"""
API routes.

These routes are automatically prefixed with /api.
"""
from caprail.routing.router import Router


def register(router: Router) -> None:
    router.get("/status", lambda req: {"status": "ok", "caprail": "0.1.0"})
'''


def _config_app(name: str) -> str:
    return f'''"""Application configuration."""
import os

config = {{
    "name": os.environ.get("APP_NAME", "{name}"),
    "env": os.environ.get("APP_ENV", "production"),
    "debug": os.environ.get("APP_DEBUG", "false").lower() in ("true", "1"),
    "url": os.environ.get("APP_URL", "http://localhost:8000"),
    "timezone": "UTC",
    "locale": "en",
    "key": os.environ.get("APP_KEY", ""),
}}
'''


def _config_database() -> str:
    return '''"""Database configuration."""
import os

config = {
    "default": os.environ.get("DB_CONNECTION", "sqlite"),
    "connections": {
        "sqlite": {
            "driver": "sqlite",
            "database": os.environ.get("DB_DATABASE", "database/database.sqlite"),
        },
        "postgresql": {
            "driver": "postgresql+asyncpg",
            "host": os.environ.get("DB_HOST", "127.0.0.1"),
            "port": int(os.environ.get("DB_PORT", "5432")),
            "database": os.environ.get("DB_DATABASE", "caprail"),
            "username": os.environ.get("DB_USERNAME", "root"),
            "password": os.environ.get("DB_PASSWORD", ""),
        },
        "mysql": {
            "driver": "mysql+aiomysql",
            "host": os.environ.get("DB_HOST", "127.0.0.1"),
            "port": int(os.environ.get("DB_PORT", "3306")),
            "database": os.environ.get("DB_DATABASE", "caprail"),
            "username": os.environ.get("DB_USERNAME", "root"),
            "password": os.environ.get("DB_PASSWORD", ""),
        },
    },
}
'''


def _config_session() -> str:
    return '''"""Session configuration."""
import os

config = {
    "driver": os.environ.get("SESSION_DRIVER", "cookie"),
    "lifetime": int(os.environ.get("SESSION_LIFETIME", "120")),
    "encrypt": False,
    "path": "/",
    "domain": None,
    "secure": False,
    "http_only": True,
    "same_site": "Lax",
}
'''


def _home_controller() -> str:
    return '''"""Home controller."""
from __future__ import annotations

from caprail.http.request import Request
from caprail.http.response import Response
from caprail.view.engine import ViewEngine


class HomeController:

    async def index(self, request: Request) -> Response:
        return await ViewEngine.instance().render_response("welcome", {
            "title": "Welcome to Caprail",
        })
'''


def _app_service_provider() -> str:
    return '''"""App service provider."""
from __future__ import annotations

from caprail.foundation.service_provider import ServiceProvider
from caprail.view.engine import ViewEngine
from caprail.orm.model import configure_database


class AppServiceProvider(ServiceProvider):

    def register(self) -> None:
        # Configure the view engine
        views_path = self.app.resources_path("views")
        ViewEngine.configure(views_path)

    def boot(self) -> None:
        # Configure the database
        self._setup_database()

    def _setup_database(self) -> None:
        import config.database as db_config
        cfg = db_config.config
        connection = cfg["default"]
        conn_cfg = cfg["connections"][connection]

        driver = conn_cfg["driver"]
        if driver == "sqlite":
            db_path = self.app.base_path / conn_cfg["database"]
            db_path.parent.mkdir(parents=True, exist_ok=True)
            url = f"sqlite+aiosqlite:///{db_path}"
        else:
            user = conn_cfg.get("username", "root")
            password = conn_cfg.get("password", "")
            host = conn_cfg.get("host", "localhost")
            port = conn_cfg.get("port", 5432)
            name = conn_cfg.get("database", "caprail")
            url = f"{driver}://{user}:{password}@{host}:{port}/{name}"

        configure_database(url, echo=self.app.is_debug())
'''


def _route_service_provider() -> str:
    return '''"""Route service provider."""
from __future__ import annotations

from caprail.foundation.service_provider import ServiceProvider


class RouteServiceProvider(ServiceProvider):

    def boot(self) -> None:
        router = self.app.router

        # Web routes
        try:
            from routes.web import register as web_register
            web_register(router)
        except ImportError:
            pass

        # API routes (prefixed with /api)
        try:
            from routes.api import register as api_register
            router.group({"prefix": "/api"}, lambda: api_register(router))
        except ImportError:
            pass
'''


def _layout_app() -> str:
    return '''<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield(\'title\') — {{ app_name }}</title>
    @vite([\'resources/css/app.css\', \'resources/js/app.js\'])
</head>
<body class="bg-gray-50 text-gray-900 antialiased" x-data>

    <nav class="bg-white border-b border-gray-200 px-6 py-4">
        <div class="max-w-7xl mx-auto flex items-center justify-between">
            <a href="/" class="text-xl font-bold text-indigo-600">{{ app_name }}</a>
            <div class="flex items-center gap-4">
                @auth
                    <span class="text-sm text-gray-600">{{ auth_user.name }}</span>
                    <a href="/logout" class="text-sm text-red-500 hover:underline">Logout</a>
                @endauth
                @guest
                    <a href="/login" class="text-sm text-gray-600 hover:underline">Login</a>
                @endguest
            </div>
        </div>
    </nav>

    <main class="max-w-7xl mx-auto px-6 py-8">
        @yield(\'content\')
    </main>

    <footer class="border-t border-gray-100 mt-16 py-6 text-center text-sm text-gray-400">
        Powered by <a href="https://caprail.dev" class="text-indigo-500 hover:underline">Caprail</a>
    </footer>

</body>
</html>
'''


def _welcome_view() -> str:
    return '''@extends(\'layouts.app\')

@section(\'title\')Welcome@endsection

@section(\'content\')
<div class="text-center py-20">
    <h1 class="text-5xl font-bold text-indigo-600 mb-4">{{ title }}</h1>
    <p class="text-xl text-gray-500 mb-8">
        The Laravel-inspired Python web framework.
    </p>
    <div class="flex justify-center gap-4">
        <a href="https://caprail.dev/docs"
           class="px-6 py-3 bg-indigo-600 text-white rounded-lg font-semibold hover:bg-indigo-700 transition">
            Documentation
        </a>
        <a href="https://github.com/caprail/caprail"
           class="px-6 py-3 bg-white border border-gray-200 rounded-lg font-semibold hover:bg-gray-50 transition">
            GitHub
        </a>
    </div>
</div>

<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-16">
    @foreach(features as feature)
    <div class="bg-white rounded-xl border border-gray-100 p-6 shadow-sm">
        <h3 class="text-lg font-semibold mb-2">{{ feature.title }}</h3>
        <p class="text-gray-500 text-sm">{{ feature.description }}</p>
    </div>
    @endforeach
</div>
@endsection
'''


def _login_view() -> str:
    return '''@extends(\'layouts.app\')

@section(\'title\')Login@endsection

@section(\'content\')
<div class="max-w-md mx-auto mt-12">
    <div class="bg-white rounded-2xl border border-gray-100 shadow-sm p-8">
        <h2 class="text-2xl font-bold mb-6 text-center">Sign In</h2>

        @if(error)
        <div class="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-600">
            {{ error }}
        </div>
        @endif

        <form method="POST" action="/login" class="space-y-4">
            @csrf

            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
                <input type="email" name="email"
                       class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
                       placeholder="you@example.com" required>
            </div>

            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">Password</label>
                <input type="password" name="password"
                       class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
                       required>
            </div>

            <button type="submit"
                    class="w-full py-2 bg-indigo-600 text-white rounded-lg font-semibold hover:bg-indigo-700 transition">
                Sign In
            </button>
        </form>
    </div>
</div>
@endsection
'''


def _app_css() -> str:
    return """@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
    html {
        font-family: 'Inter', system-ui, sans-serif;
    }
}

@layer components {
    .btn {
        @apply px-4 py-2 rounded-lg font-medium transition-colors duration-150;
    }
    .btn-primary {
        @apply btn bg-indigo-600 text-white hover:bg-indigo-700;
    }
    .btn-secondary {
        @apply btn bg-gray-100 text-gray-800 hover:bg-gray-200;
    }
}
"""


def _app_js() -> str:
    return """import './bootstrap';
import Alpine from 'alpinejs';

window.Alpine = Alpine;
Alpine.start();
"""


def _vite_config() -> str:
    return """import { defineConfig } from 'vite';
import { resolve } from 'path';

export default defineConfig({
    build: {
        outDir: 'public/build',
        rollupOptions: {
            input: {
                app: resolve(__dirname, 'resources/js/app.js'),
            },
        },
    },
    server: {
        cors: true,
        port: 5173,
    },
});
"""


def _tailwind_config() -> str:
    return """/** @type {import('tailwindcss').Config} */
export default {
    content: [
        './resources/views/**/*.blade.html',
        './resources/js/**/*.js',
    ],
    theme: {
        extend: {
            fontFamily: {
                sans: ['Inter', 'system-ui', 'sans-serif'],
            },
        },
    },
    plugins: [],
};
"""


def _package_json(name: str) -> str:
    return f"""{{
  "name": "{name}",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {{
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }},
  "dependencies": {{
    "alpinejs": "^3.14.0"
  }},
  "devDependencies": {{
    "@tailwindcss/forms": "^0.5.9",
    "@tailwindcss/typography": "^0.5.15",
    "autoprefixer": "^10.4.20",
    "postcss": "^8.4.49",
    "tailwindcss": "^3.4.17",
    "vite": "^6.0.5"
  }}
}}
"""


def _test_example() -> str:
    return '''"""Example test."""
import pytest


@pytest.mark.asyncio
async def test_example() -> None:
    assert 1 + 1 == 2
'''
