import sys
import os

def create(name):
    # si no esta ubicado en la carpeta del proyecto que debe contener un docker-compose.yaml para toda la ejecucion
    if not os.path.isfile(f"{os.getcwd()}/docker-compose.yaml"):
        print("[INFO] No se encuentra en la carpeta del proyecto.")
        sys.exit()

    estructura = [
        "app/components/filter/domain/value_objects",
        "app/components/filter/infrastructure/dto",
        "app/Traits",
        "commands",
        "config",
        "config/translate",
        "database",
        "middleware",
        "middleware/exceptions",
        "database/migrations",
    ]

    archivos = {
        '.env': (
            "# Configuración de la base de datos\n"
            "DB_USER=postgres\n"
            "DB_PASSWORD=postgres\n"
            "DB_HOST=db\n"
            "DB_PORT=5432\n"
            "DB_NAME=quipusdb\n"
            "\n"
            "# Configuración de Token de validación\n"
            "SECRET_TOKEN=123456789\n"
        ),
        'alembic.ini': (
            "[alembic]\n"
            "script_location = database\n"
            "\n"
            "prepend_sys_path = .\n"
            "\n"
            "version_locations = %(here)s/database/migrations\n"
            "\n"
            "version_path_separator = os\n"
            "\n"
            "recursive_version_locations = false\n"
            "\n"
            "sqlalchemy.url =\n"
            "\n"
            "[post_write_hooks]\n"
            "[loggers]\n"
            "keys = root,sqlalchemy,alembic\n"
            "\n"
            "[handlers]\n"
            "keys = console\n"
            "\n"
            "[formatters]\n"
            "keys = generic\n"
            "\n"
            "[logger_root]\n"
            "level = WARNING\n"
            "handlers = console\n"
            "qualname =\n"
            "\n"
            "[logger_sqlalchemy]\n"
            "level = WARNING\n"
            "handlers =\n"
            "qualname = sqlalchemy.engine\n"
            "\n"
            "[logger_alembic]\n"
            "level = INFO\n"
            "handlers =\n"
            "qualname = alembic\n"
            "\n"
            "[handler_console]\n"
            "class = StreamHandler\n"
            "args = (sys.stderr,)\n"
            "level = NOTSET\n"
            "formatter = generic\n"
            "\n"
            "[formatter_generic]\n"
            "format = %(levelname)-5.5s [%(name)s] %(message)s\n"
            "datefmt = %H:%M:%S\n"
        ),
        "command":(
            "from typing import Dict, Any\n"
            "from pathlib import Path\n"
            "import importlib.util\n"
            "import argparse\n"
            "import sys\n"
            "\n"
            "COMMANDS_DIR = Path(__file__).parent / \"commands\"\n"
            "\n"
            "\n"
            "def load_commands() -> Dict[str, Any]:\n"
            "    commands = {}\n"
            "    command_aliases = {}\n"
            "\n"
            "    for file in COMMANDS_DIR.rglob(\"*.py\"):\n"
            "        if file.name.startswith(\"__\") or not file.suffix == \".py\":\n"
            "            continue\n"
            "\n"
            "        module_name = file.with_suffix('').relative_to(COMMANDS_DIR).as_posix().replace(\"/\", \"_\")\n"
            "        module = _import_module_from_path(file, module_name)\n"
            "\n"
            "        if hasattr(module, \"Command\"):\n"
            "            command_instance = module.Command()\n"
            "            commands[command_instance.name] = command_instance\n"
            "\n"
            "            for alias in getattr(command_instance, \"commands\", []):\n"
            "                command_aliases[alias] = command_instance.name\n"
            "                commands[alias] = command_instance\n"
            "\n"
            "    return commands, command_aliases\n"
            "\n"
            "\n"
            "def _import_module_from_path(file_path: Path, module_name: str):\n"
            "    spec = importlib.util.spec_from_file_location(module_name, str(file_path))\n"
            "    if spec is None or spec.loader is None:\n"
            "        raise ImportError(f\"Unable to load spec for module {module_name}\")\n"
            "\n"
            "    module = importlib.util.module_from_spec(spec)\n"
            "    sys.modules[module_name] = module\n"
            "    spec.loader.exec_module(module)\n"
            "    return module\n"
            "\n"
            "\n"
            "def create_parser(commands: Dict[str, Any]) -> argparse.ArgumentParser:\n"
            "    parser = argparse.ArgumentParser(prog=\"command\", description=\"CLI for managing FastAPI\")\n"
            "    subparsers = parser.add_subparsers(dest=\"command\")\n"
            "\n"
            "    for name, command_obj in commands.items():\n"
            "        sub_parser = subparsers.add_parser(name, help=getattr(command_obj, \"help\", \"\"))\n"
            "        sub_parser.set_defaults(func=command_obj.handle)\n"
            "\n"
            "    return parser\n"
            "\n"
            "\n"
            "def main():\n"
            "    if len(sys.argv) < 2:\n"
            "        print(\"[ERROR] No command provided.\n\")\n"
            "        print(\"Available commands:\")\n"
            "        _print_available_commands()\n"
            "        sys.exit(1)\n"
            "\n"
            "    commands, aliases = load_commands()\n"
            "    original_args = sys.argv[1:]\n"
            "\n"
            "    # Handle alias remapping\n"
            "    if original_args[0] in aliases:\n"
            "        aliased_command = aliases[original_args[0]]\n"
            "        original_args = [aliased_command, original_args[0]] + original_args[1:]\n"
            "\n"
            "    parser = create_parser(commands)\n"
            "    args, unknown_args = parser.parse_known_args(original_args)\n"
            "\n"
            "    if hasattr(args, \"func\"):\n"
            "        args.func(unknown_args)\n"
            "    else:\n"
            "        parser.print_help()\n"
            "        sys.exit(1)\n"
            "\n"
            "\n"
            "def _print_available_commands():\n"
            "    commands, _ = load_commands()\n"
            "    for name in sorted(commands.keys()):\n"
            "        print(f\"  - {name}\")\n"
            "\n"
            "\n"
            "if __name__ == \"__main__\":\n"
            "    main()\n"
        ),
        'Dockerfile':(
            "FROM python:3.13.2-slim\n"
            "WORKDIR /app\n"
            "COPY . .\n"
            "RUN pip install --no-cache-dir -r requirements.txt\n"
            "EXPOSE 5000\n"
            "CMD [\"uvicorn\", \"app.main:run\", \"--host\", \"0.0.0.0\", \"--port\", \"5000\", \"--reload\"]\n"
        ),
        'requirements.txt': (
            "fastapi==0.115.12\n"
            "pydantic==2.11.3\n"
            "pydantic_core==2.33.1\n"
            "python-dotenv==1.1.0\n"
            "uvicorn==0.34.0\n"
            "sqlalchemy\n"
            "sqlmodel\n"
            "alembic\n"
            "psycopg2-binary\n"
            "pydantic-settings\n"
            "pyjwt\n"
            "passlib[bcrypt]\n"
            "email-validator\n"
            "importlib_metadata\n"
        ),
        'README.md': (
            "# Proyecto FastAPI\n"
            "Este es un proyecto FastAPI con Docker y PostgreSQL.\n"
            "\n"
            "## Estructura del Proyecto\n"
            "- app: Contiene la lógica de la aplicación.\n"
            "- config: Configuraciones del proyecto.\n"
            "- database: Configuraciones de la base de datos y migraciones.\n"
        ),
        'app/main.py': (
            "from starlette.exceptions import HTTPException as StarletteHTTPException\n"
            "from fastapi.exceptions import RequestValidationError\n"
            "from fastapi import FastAPI\n"
            "\n"
            "# This file is part of the middleware project.\n"
            "from middleware.validation import Validation as ValidationMiddleware\n"
            "from middleware.exceptions.handler import ExceptionHandler\n"
            "\n"
            "run = FastAPI()\n"
            "\n"
            "# Middleware for token validation and handler errors\n"
            "run.add_exception_handler(RequestValidationError, ExceptionHandler.validation_exception)\n"
            "run.add_exception_handler(StarletteHTTPException, ExceptionHandler.http_exception)\n"
            "run.add_exception_handler(Exception, ExceptionHandler.generic_exception)\n"
            "run.add_middleware(ValidationMiddleware)\n"
            "\n"
            "@run.get(\"/\")\n"
            "async def root():\n"
            "    return {\"message\": \"Hello World\"}\n"
        ),
        'app/components/filter/domain/filter.py': (
            "from typing import List, Any, Literal\n"
            "from pydantic import BaseModel\n"
            "\n"
            "class FilterConstraint(BaseModel):\n"
            "    value: Any\n"
            "    matchMode: Literal[\n"
            "        \"startsWith\", \"contains\", \"endsWith\",\n"
            "        \"equals\", \"notEquals\", \"in\", \"lt\", \"lte\", \"gt\", \"gte\"\n"
            "    ]\n"
            "\n"
            "class FieldFilter(BaseModel):\n"
            "    operator: Literal[\"and\", \"or\"] = \"and\"\n"
            "    constraints: List[FilterConstraint]\n"
        ),
        'app/components/filter/domain/value_objects/filter.py': (
            "from pydantic import BaseModel\n"
            "from typing import Dict, Any, Optional\n"
            "\n"
            "from app.components.filter.domain.filter import FieldFilter\n"
            "\n"
            "class FiltersValueObject(BaseModel):\n"
            "    global_: Optional[FieldFilter] = None  # usamos global_ por ser palabra reservada\n"
            "    other_filters: Dict[str, FieldFilter] = {}\n"
            "\n"
            "    @classmethod\n"
            "    def from_raw(cls, raw: Optional[Dict[str, Any]]) -> \"FiltersValueObject\":\n"
            "        if not raw:\n"
            "            return cls()\n"
            "\n"
            "        global_filter = raw.get(\"global\")\n"
            "        others = {k: v for k, v in raw.items() if k != \"global\"}\n"
            "\n"
            "        return cls(\n"
            "            global_=FieldFilter(**global_filter) if global_filter else None,\n"
            "            other_filters={k: FieldFilter(**v) for k, v in others.items()}\n"
            "        )\n"
            "\n"
            "    def as_dict(self) -> Dict[str, Any]:\n"
            "        result = {}\n"
            "        if self.global_:\n"
            "            result[\"global\"] = self.global_.dict()\n"
            "        result.update({k: v.dict() for k, v in self.other_filters.items()})\n"
            "        return result\n"
        ),
        'app/components/filter/infrastructure/dto/input_filter.py': (
            "from pydantic import BaseModel\n"
            "from typing import Optional, Dict, Any\n"
            "\n"
            "from app.components.filter.domain.value_objects.filter import FiltersValueObject\n"
            "\n"
            "class InputFilter(BaseModel):\n"
            "    trashed: int = 0\n"
            "    paginate: int = 0\n"
            "    page: int = 1\n"
            "    rows: int = 25\n"
            "    filters: FiltersValueObject = FiltersValueObject()\n"
            "\n"
            "    @classmethod\n"
            "    def from_request_body(cls, body: Optional[Dict[str, Any]] = None) -> \"InputFilter\":\n"
            "        if body is None:\n"
            "            body = {}\n"
            "\n"
            "        return cls(\n"
            "            trashed= 1 if body.get(\"trashed\", 0) != 0 else body.get(\"trashed\", 0),\n"
            "            paginate= 1 if body.get(\"paginate\", 0) != 0 else body.get(\"paginate\", 0),\n"
            "            page= 1 if body.get(\"page\", 0) == 0 else body.get(\"page\", 0),\n"
            "            rows=body.get(\"rows\", 25),\n"
            "            filters=FiltersValueObject.from_raw(body.get(\"filters\"))\n"
            "        )\n"
        ),
        "app/Traits/filters.py": (
            "from sqlalchemy.orm import Query\n"
            "from sqlalchemy import or_, and_, not_\n"
            "from typing import List, Dict, Any, Union, Callable, Optional\n"
            "\n"
            "class Filterable:\n"
            "    def __init__(self):\n"
            "        self.columns_change = {}\n"
            "\n"
            "    def apply_filters(self, query: Query, filters: Dict[str, Any], global_fields: Optional[Dict[str, Any]] = None) -> Query:\n"
            "        #self.columns_change = {field: field for field in global_fields}\n"
            "        self.columns_change = global_fields or {}\n"
            "        # Global filter\n"
            "        if filters['global_'] is not None:\n"
            "            fields = list(self.columns_change.values())\n"
            "            query = self.apply_global_filter(query, filters['global_'], fields)\n"
            "        # Field filters\n"
            "        for field, filter_data in filters.items():\n"
            "            if field in self.columns_change:\n"
            "                query = self.apply_field_filter(query, self.columns_change[field], filter_data)\n"
            "        return query\n"
            "\n"
            "    def apply_global_filter(self, query: Query, global_filter: Dict[str, Any], global_fields: List[str]) -> Query:\n"
            "        operator = global_filter.get('operator', 'or')\n"
            "        constraints = global_filter['constraints']\n"
            "        condition_group = []\n"
            "        for constraint in constraints:\n"
            "            field_conditions = []\n"
            "            for field in global_fields:\n"
            "                condition = self.apply_constraint_to_field(field, constraint)\n"
            "                field_conditions.append(condition)\n"
            "            condition_group.append(or_(*field_conditions) if operator == 'or' else and_(*field_conditions))\n"
            "        return query.filter(or_(*condition_group))\n"
            "\n"
            "    def apply_field_filter(self, query: Query, field: str, filter_data: Dict[str, Any]) -> Query:\n"
            "        operator = filter_data.get('operator', 'and')\n"
            "        constraints = filter_data['constraints']\n"
            "        real_field = self.columns_change.get(field, field)\n"
            "        field_conditions = []\n"
            "        for constraint in constraints:\n"
            "            condition = self.apply_constraint_to_field(real_field, constraint)\n"
            "            field_conditions.append(condition)\n"
            "        if operator == 'or':\n"
            "            return query.filter(or_(*field_conditions))\n"
            "        else:\n"
            "            return query.filter(and_(*field_conditions))\n"
            "\n"
            "    def apply_constraint_to_field(self, field: Union[str, Callable], constraint: Dict[str, Any]):\n"
            "        value = constraint.get('value')\n"
            "        match_mode = constraint.get('matchMode', 'equals')\n"
            "        condition_func = self.resolve_match_mode(match_mode, value)\n"
            "        return condition_func(field)\n"
            "\n"
            "    def resolve_match_mode(self, match_mode: str, value: Any) -> Callable:\n"
            "        def func(field):\n"
            "            if match_mode == 'startsWith':\n"
            "                return field.startswith(value)\n"
            "            elif match_mode == 'contains':\n"
            "                return field.contains(value)\n"
            "            elif match_mode == 'endsWith':\n"
            "                return field.endswith(value)\n"
            "            elif match_mode == 'equals':\n"
            "                return field == value\n"
            "            elif match_mode == 'notEquals':\n"
            "                return field != value\n"
            "            elif match_mode == 'in':\n"
            "                return field.in_(value if isinstance(value, list) else [value])\n"
            "            elif match_mode == 'lt':\n"
            "                return field < value\n"
            "            elif match_mode == 'lte':\n"
            "                return field <= value\n"
            "            elif match_mode == 'gt':\n"
            "                return field > value\n"
            "            elif match_mode == 'gte':\n"
            "                return field >= value\n"
            "            else:\n"
            "                raise ValueError(f\"Match mode '{match_mode}' no soportado.\")\n"
            "        return func\n"
        ),
        "commands/seeder.py":(
            "from typing import Type, List\n"
            "from types import ModuleType\n"
            "from pathlib import Path\n"
            "from glob import glob\n"
            "import importlib\n"
            "import argparse\n"
            "import inspect\n"
            "import sys\n"
            "import os\n"
            "\n"
            "from database.seeders.base import BaseSeeder\n"
            "\n"
            "class Command:\n"
            "    name = \"seed\"\n"
            "    help = \"Seed the database with initial data\"\n"
            "    description = (\n"
            "        \"This command will seed the database with initial data. \"\n"
            "        \"It will create a superuser and some default roles and permissions.\"\n"
            "    )\n"
            "    commands = [\"seed:all\"]\n"
            "\n"
            "    def handle(self, args: List[str]):\n"
            "        parser = self._build_parser()\n"
            "        parsed_args, _ = parser.parse_known_args(args)\n"
            "\n"
            "        if parsed_args.class_name:\n"
            "            self.run_seeder_class(parsed_args.class_name)\n"
            "            sys.exit(0)\n"
            "\n"
            "        if parsed_args.cmd == \"seed:all\":\n"
            "            self.run_all_seeders()\n"
            "            sys.exit(0)\n"
            "\n"
            "        parser.print_help()\n"
            "        sys.exit(1)\n"
            "\n"
            "    def _build_parser(self) -> argparse.ArgumentParser:\n"
            "        parser = argparse.ArgumentParser(description=self.description)\n"
            "        parser.add_argument(\"cmd\", nargs=\"?\", help=\"Comando: seed --class=SeederName o seed:all\")\n"
            "        parser.add_argument(\"--class\", dest=\"class_name\", required=False, help=\"Class name to run\")\n"
            "        return parser\n"
            "\n"
            "    def run_all_seeders(self):\n"
            "        seeder_classes = self._discover_seeders()\n"
            "\n"
            "        if not seeder_classes:\n"
            "            print(\"[WARNING] No seeders found.\")\n"
            "            return\n"
            "\n"
            "        total = 0\n"
            "        for cls in seeder_classes:\n"
            "            total += self._run_seeder_instance(cls)\n"
            "\n"
            "        print(f\"[INFO] Total seeders run: {total}\")\n"
            "\n"
            "    def run_seeder_class(self, class_name: str):\n"
            "        seeder_classes = self._discover_seeders()\n"
            "\n"
            "        for cls in seeder_classes:\n"
            "            if cls.__name__ == class_name:\n"
            "                self._run_seeder_instance(cls)\n"
            "                return\n"
            "\n"
            "        print(f\"[ERROR] Seeder class '{class_name}' not found.\")\n"
            "        sys.exit(1)\n"
            "\n"
            "    def _discover_seeders(self) -> List[Type[BaseSeeder]]:\n"
            "        base_dir = Path(__file__).resolve().parent.parent\n"
            "        seeder_files = glob(str(base_dir / \"database\" / \"seeders\" / \"**\" / \"*.py\"), recursive=True)\n"
            "\n"
            "        seeder_classes = []\n"
            "\n"
            "        for file_path in seeder_files:\n"
            "            if file_path.endswith(\"base.py\"):\n"
            "                continue\n"
            "\n"
            "            module_path = self._convert_path_to_module(file_path, base_dir)\n"
            "\n"
            "            try:\n"
            "                module = importlib.import_module(module_path)\n"
            "                seeder_classes.extend(self._get_seeder_classes_from_module(module))\n"
            "            except ImportError as e:\n"
            "                print(f\"[ERROR] Cannot import {module_path}: {e}\")\n"
            "\n"
            "        return seeder_classes\n"
            "\n"
            "    def _convert_path_to_module(self, file_path: str, base_dir: Path) -> str:\n"
            "        relative_path = os.path.relpath(file_path, base_dir)\n"
            "        return relative_path.replace(os.path.sep, \".\").replace(\".py\", \"\")\n"
            "\n"
            "    def _get_seeder_classes_from_module(self, module: ModuleType) -> List[Type[BaseSeeder]]:\n"
            "        return [\n"
            "            obj for _, obj in inspect.getmembers(module, inspect.isclass)\n"
            "            if issubclass(obj, BaseSeeder) and obj is not BaseSeeder\n"
            "        ]\n"
            "\n"
            "    def _run_seeder_instance(self, seeder_class: Type[BaseSeeder]) -> int:\n"
            "        print(f\"[RUNNING] Executing {seeder_class.__name__}...\")\n"
            "        try:\n"
            "            instance = seeder_class()\n"
            "            instance.run()\n"
            "            return 1\n"
            "        except Exception as e:\n"
            "            print(f\"[ERROR] Failed to run {seeder_class.__name__}: {e}\")\n"
            "            return 0\n"
        ),
        "config/database.py": (
            "from sqlalchemy.ext.declarative import declarative_base\n"
            "from sqlalchemy.orm import sessionmaker\n"
            "from sqlalchemy import create_engine\n"
            "\n"
            "from config.settings import Settings\n"
            "\n"
            "class Database:\n"
            "    def __init__(self):\n"
            "        self.settings = Settings()\n"
            "        self.engine = create_engine(f\"postgresql+psycopg2://{self.settings.DB_USER}:{self.settings.DB_PASSWORD}@{self.settings.DB_HOST}:{self.settings.DB_PORT}/{self.settings.DB_NAME}\", echo=False)\n"
            "        self.Session = sessionmaker(bind=self.engine, autocommit=False, autoflush=False)\n"
            "        self.Base = declarative_base()\n"
            "\n"
            "    def get_session(self):\n"
            "        return self.Session()\n"
            "\n"
            "    def get_base(self):\n"
            "        return self.Base\n"
            "\n"
            "    def get_engine(self):\n"
            "        return self.engine\n"
            "\n"
            "    def get_db(self):\n"
            "        db = self.get_session()\n"
            "        try:\n"
            "            yield db\n"
            "        finally:\n"
            "            db.close()\n"
            "\n"
            "database = Database()\n"
            "Base = database.get_base()\n"
            "engine = database.get_engine()\n"
        ),
        "config/settings.py": (
            "from pydantic_settings import BaseSettings\n"
            "\n"
            "class Settings(BaseSettings):\n"
            "    # Database settings\n"
            "    DB_HOST: str\n"
            "    DB_PORT: int\n"
            "    DB_NAME: str\n"
            "    DB_USER: str\n"
            "    DB_PASSWORD: str\n"
            "    # Token settings\n"
            "    SECRET_TOKEN: str\n"
            "\n"
            "    class Config:\n"
            "        env_file = \".env\"\n"
            "        env_file_encoding = \"utf-8\"\n"
        ),
        "config/translate/messages.py": (
            "MESSAGES = {\n"
            "    \"es\": {\n"
            "        \"value is not a valid email address\": \"El campo {field} no es un correo válido.\",\n"
            "        \"field required\": \"El campo {field} es obligatorio.\",\n"
            "        \"str type expected\": \"El {field} debe ser un texto\",\n"
            "        \"value is not a valid integer\": \"El campo {field} debe ser un número entero\",\n"
            "        \"value is not a valid float\": \"El campo {field} debe ser un número decimal\",\n"
            "    },\n"
            "    \"en\": {\n"
            "        \"value is not a valid email address\": \"The field {field} is invalid email address.\",\n"
            "        \"field required\": \"The field {field} is required.\",\n"
            "        \"str type expected\": \"The field {field} must be a string.\",\n"
            "        \"value is not a valid integer\": \"The field {field} must be a integer.\",\n"
            "        \"value is not a valid float\": \"The field {field} must be a float.\",\n"
            "    },\n"
            "}\n"
        ),
        "database/env.py": (
            "from logging.config import fileConfig\n"
            "from sqlalchemy import engine_from_config\n"
            "from sqlalchemy import pool\n"
            "from alembic import context\n"
            "\n"
            "from config.settings import Settings\n"
            "from config.database import Base\n"
            "\n"
            "# this is the Alembic Config object, which provides\n"
            "# access to the values within the .ini file in use.\n"
            "config = context.config\n"
            "settings = Settings()\n"
            "config.set_main_option(\n"
            "    \"sqlalchemy.url\",\n"
            "    f\"postgresql+psycopg2://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}\",\n"
            ")\n"
            "# Interpret the config file for Python logging.\n"
            "# This line sets up loggers basically.\n"
            "if config.config_file_name is not None:\n"
            "    fileConfig(config.config_file_name)\n"
            "# add your model's MetaData object here\n"
            "# for 'autogenerate' support\n"
            "# from myapp import mymodel\n"
            "# target_metadata = mymodel.Base.metadata\n"
            "target_metadata = Base.metadata\n"
            "# other values from the config, defined by the needs of env.py,\n"
            "# can be acquired:\n"
            "# my_important_option = config.get_main_option(\"my_important_option\")\n"
            "# ... etc.\n"
            "def run_migrations_offline() -> None:\n"
            "    \"\"\"Run migrations in 'offline' mode.\n"
            "    This configures the context with just a URL\n"
            "    and not an Engine, though an Engine is acceptable\n"
            "    here as well.  By skipping the Engine creation\n"
            "    we don't even need a DBAPI to be available.\n"
            "    Calls to context.execute() here emit the given string to the\n"
            "    script output.\n"
            "    \"\"\"\n"
            "    url = config.get_main_option(\"sqlalchemy.url\")\n"
            "    context.configure(\n"
            "        url=url,\n"
            "        target_metadata=target_metadata,\n"
            "        literal_binds=True,\n"
            "        dialect_opts={\"paramstyle\": \"named\"},\n"
            "    )\n"
            "    with context.begin_transaction():\n"
            "        context.run_migrations()\n"
            "def run_migrations_online() -> None:\n"
            "    \"\"\"Run migrations in 'online' mode.\n"
            "    In this scenario we need to create an Engine\n"
            "    and associate a connection with the context.\n"
            "    \"\"\"\n"
            "    connectable = engine_from_config(\n"
            "        config.get_section(config.config_ini_section, {}),\n"
            "        prefix=\"sqlalchemy.\",\n"
            "        poolclass=pool.NullPool,\n"
            "    )\n"
            "    with connectable.connect() as connection:\n"
            "        context.configure(\n"
            "            connection=connection,\n"
            "            target_metadata=target_metadata,\n"
            f"            version_table='migrations_{name}'\n"
            "        )\n"
            "        with context.begin_transaction():\n"
            "            context.run_migrations()\n"
            "if context.is_offline_mode():\n"
            "    run_migrations_offline()\n"
            "else:\n"
            "    run_migrations_online()\n"
        ),
        "database/script.py.mako": (
            "\"\"\"${message}\n"
            "\n"
            "Revision ID: ${up_revision}\n"
            "Revises: ${down_revision | comma,n}\n"
            "Create Date: ${create_date}\n"
            "\n"
            "\"\"\"\n"
            "from typing import Sequence, Union\n"
            "\n"
            "from alembic import op\n"
            "import sqlalchemy as sa\n"
            "${imports if imports else \"\"}\n"
            "\n"
            "# revision identifiers, used by Alembic.\n"
            "revision: str = ${repr(up_revision)}\n"
            "down_revision: Union[str, None] = ${repr(down_revision)}\n"
            "branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}\n"
            "depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}\n"
            "\n"
            "\n"
            "def upgrade() -> None:\n"
            "    \"\"\"Upgrade schema.\"\"\"\n"
            "    ${upgrades if upgrades else \"pass\"}\n"
            "\n"
            "\n"
            "def downgrade() -> None:\n"
            "    \"\"\"Downgrade schema.\"\"\"\n"
            "    ${downgrades if downgrades else \"pass\"}\n"
        ),
        "middleware/validation.py": (
            "from starlette.middleware.base import BaseHTTPMiddleware\n"
            "from fastapi.responses import JSONResponse\n"
            "from fastapi import Request\n"
            "import os\n"
            "\n"
            "from config.settings import Settings\n"
            "\n"
            "class Validation(BaseHTTPMiddleware):\n"
            "    async def dispatch(self, request: Request, call_next):\n"
            "        settings = Settings()\n"
            "\n"
            "        token_header = request.headers.get(\"validate\")\n"
            "        expected_token = settings.SECRET_TOKEN\n"
            "\n"
            "        if not token_header or token_header != expected_token:\n"
            "            return JSONResponse(status_code=401, content={\"message\": \"Token no válido\"})\n"
            "\n"
            "        response = await call_next(request)\n"
            "        return response\n"
        ),
        "middleware/exceptions/handler.py": (
            "from fastapi import Request, HTTPException\n"
            "from fastapi.responses import JSONResponse\n"
            "from fastapi.exceptions import RequestValidationError\n"
            "from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR, HTTP_422_UNPROCESSABLE_ENTITY\n"
            "import logging\n"
            "import uuid\n"
            "\n"
            "from config.translate.messages import MESSAGES\n"
            "\n"
            "logger = logging.getLogger(\"uvicorn.error\")\n"
            "\n"
            "class ExceptionHandler:\n"
            "\n"
            "    @staticmethod\n"
            "    def generate_trace_id() -> str:\n"
            "        return str(uuid.uuid4())\n"
            "\n"
            "    @staticmethod\n"
            "    def get_language(request: Request) -> str:\n"
            "        return request.headers.get(\"accept-language\", \"es\").lower().split(\",\")[0].strip()\n"
            "\n"
            "    @staticmethod\n"
            "    def format_validation_errors(errors: list[dict], lang: str = 'es') -> dict[str, list[str]]:\n"
            "        messages = MESSAGES.get(lang, MESSAGES[\"es\"])\n"
            "        formatted = {}\n"
            "        for err in errors:\n"
            "            loc = err.get(\"loc\", [])\n"
            "            field = loc[1] if len(loc) >= 2 and loc[0] == \"body\" else \".\".join(str(l) for l in loc)\n"
            "            raw_message = err.get(\"msg\", \"Error desconocido\").split(\":\")[0].lower().strip()\n"
            "            tempalte = messages.get(raw_message, raw_message)\n"
            "\n"
            "            try:\n"
            "                message = tempalte.format(field=field)\n"
            "            except Exception:\n"
            "                message = tempalte\n"
            "            formatted.setdefault(field, []).append(message)\n"
            "        return formatted\n"
            "\n"
            "    @staticmethod\n"
            "    async def log_exception(\n"
            "        trace_id,\n"
            "        request: Request,\n"
            "        exc: Exception,\n"
            "        level: str = \"error\"\n"
            "    ):\n"
            "        method = request.method\n"
            "        url = request.url.path\n"
            "        try:\n"
            "            body = await request.json()\n"
            "        except Exception:\n"
            "            body = \"<no-body>\"\n"
            "\n"
            "        msg = f\"[{trace_id}] [{method}] {url} | Body: {body} | Error: {str(exc)}\"\n"
            "\n"
            "        if level == \"error\":\n"
            "            logger.error(msg)\n"
            "        elif level == \"warning\":\n"
            "            logger.warning(msg)\n"
            "        else:\n"
            "            logger.info(msg)\n"
            "\n"
            "    @classmethod\n"
            "    async def http_exception(cls, request: Request, exc: HTTPException):\n"
            "        trace_id = cls.generate_trace_id()\n"
            "        await cls.log_exception(trace_id, request, exc, level=\"warning\")\n"
            "        return JSONResponse(\n"
            "            status_code=exc.status_code,\n"
            "            content={\n"
            "                \"errors\": exc.detail,\n"
            "                \"error_code\": \"HTTP_ERROR\",\n"
            "                \"trace_id\": trace_id,\n"
            "            }\n"
            "        )\n"
            "\n"
            "    @classmethod\n"
            "    async def validation_exception(cls, request: Request, exc: RequestValidationError):\n"
            "        trace_id = cls.generate_trace_id()\n"
            "        await cls.log_exception(trace_id, request, exc, level=\"warning\")\n"
            "        lang = cls.get_language(request)\n"
            "        return JSONResponse(\n"
            "            status_code=HTTP_422_UNPROCESSABLE_ENTITY,\n"
            "            content={\n"
            "                \"errors\": cls.format_validation_errors(exc.errors(), lang),\n"
            "                \"error_code\": \"VALIDATION_ERROR\",\n"
            "                \"trace_id\": trace_id,\n"
            "            }\n"
            "        )\n"
            "\n"
            "    @classmethod\n"
            "    async def generic_exception(cls, request: Request, exc: Exception):\n"
            "        trace_id = cls.generate_trace_id()\n"
            "        await cls.log_exception(trace_id, request, exc, level=\"error\")\n"
            "        return JSONResponse(\n"
            "            status_code=HTTP_500_INTERNAL_SERVER_ERROR,\n"
            "            content={\n"
            "                \"message\": \"Error interno del servidor\",\n"
            "                \"error_code\": \"INTERNAL_SERVER_ERROR\",\n"
            "                \"trace_id\": trace_id,\n"
            "            }\n"
            "        )\n"
        ),
    }

    # Si el nombre del microservicio ya existe, no lo creamos
    if os.path.exists(name):
        print(f"[INFO] El microservicio '{name}' ya existe.")
        return

    os.makedirs(name, exist_ok=True)

    # Crear subdirectorios
    for carpeta in estructura:
        os.makedirs(os.path.join(name, carpeta), exist_ok=True)

    # Crear archivos
    for archivo, content in archivos.items():
        ruta_archivo = os.path.join(name, archivo)
        with open(ruta_archivo, "w", encoding="utf-8") as f:
            f.write(content)

    print(f"[OK] El microservicio '{name}' creado con éxito.")