"""
Async CRUD repository generator.

Generates repository pattern classes with async SQLAlchemy operations.
"""

from ..base import BaseGenerator
from ...ir.models import GeneratedFile, ParsedModel


class CRUDGenerator(BaseGenerator):
    """
    Generates async CRUD repository classes.

    Creates repository pattern implementation for each model
    with standard CRUD operations using AsyncSession.
    """

    def generate(self) -> list[GeneratedFile]:
        """Generate CRUD files for all apps."""
        files: list[GeneratedFile] = []

        for app_label, models in self.context.group_models_by_app().items():
            content = self._generate_crud_file(app_label, models)
            files.append(self.create_file(f"{app_label}/crud.py", content))

        return files

    def _generate_crud_file(self, app_label: str, models: list[ParsedModel]) -> str:
        """Generate crud.py content for an app."""
        model_names = [m.name for m in models]
        model_imports = ", ".join(model_names)

        # Header
        lines = [
            '"""',
            f"Async CRUD repositories for {app_label}.",
            "",
            "Auto-generated by django-cfg FastAPI ORM Generator.",
            '"""',
            "",
            "from typing import List, Optional, Sequence",
            "",
            "from sqlalchemy import select",
            "from sqlalchemy.ext.asyncio import AsyncSession",
            "",
            f"from .models import {model_imports}",
            "",
            "",
        ]

        # Generate repository for each model
        for model in models:
            repo = self._generate_repository(model)
            lines.append(repo)

        return "\n".join(lines)

    def _generate_repository(self, model: ParsedModel) -> str:
        """Generate repository class for a model."""
        suffix = self.config.repository_suffix
        class_name = f"{model.name}{suffix}"

        lines = []
        lines.append(f"class {class_name}:")
        if self.config.add_docstrings:
            lines.append(f'    """Repository for {model.name} CRUD operations."""')
        lines.append("")

        # get_by_id
        lines.extend(self._generate_get_by_id(model))
        lines.append("")

        # get_all
        lines.extend(self._generate_get_all(model))
        lines.append("")

        # get_by_ids
        lines.extend(self._generate_get_by_ids(model))
        lines.append("")

        # create
        lines.extend(self._generate_create(model))
        lines.append("")

        # create_many
        lines.extend(self._generate_create_many(model))
        lines.append("")

        # update
        lines.extend(self._generate_update(model))
        lines.append("")

        # delete
        lines.extend(self._generate_delete(model))
        lines.append("")

        # exists
        lines.extend(self._generate_exists(model))
        lines.append("")

        # count
        lines.extend(self._generate_count(model))
        lines.append("")
        lines.append("")

        return "\n".join(lines)

    def _generate_get_by_id(self, model: ParsedModel) -> list[str]:
        """Generate get_by_id method."""
        return [
            "    @staticmethod",
            f"    async def get_by_id(session: AsyncSession, id: int) -> Optional[{model.name}]:",
            f'        """Get {model.name} by ID."""',
            f"        statement = select({model.name}).where({model.name}.id == id)",
            "        result = await session.execute(statement)",
            "        return result.scalars().first()",
        ]

    def _generate_get_all(self, model: ParsedModel) -> list[str]:
        """Generate get_all method with pagination."""
        return [
            "    @staticmethod",
            "    async def get_all(",
            "        session: AsyncSession,",
            "        skip: int = 0,",
            "        limit: int = 100,",
            f"    ) -> Sequence[{model.name}]:",
            f'        """Get all {model.name} records with pagination."""',
            f"        statement = select({model.name}).offset(skip).limit(limit)",
            "        result = await session.execute(statement)",
            "        return result.scalars().all()",
        ]

    def _generate_get_by_ids(self, model: ParsedModel) -> list[str]:
        """Generate get_by_ids method."""
        return [
            "    @staticmethod",
            f"    async def get_by_ids(session: AsyncSession, ids: List[int]) -> Sequence[{model.name}]:",
            f'        """Get multiple {model.name} records by IDs."""',
            f"        statement = select({model.name}).where({model.name}.id.in_(ids))",
            "        result = await session.execute(statement)",
            "        return result.scalars().all()",
        ]

    def _generate_create(self, model: ParsedModel) -> list[str]:
        """Generate create method."""
        return [
            "    @staticmethod",
            f"    async def create(session: AsyncSession, obj: {model.name}) -> {model.name}:",
            f'        """Create a new {model.name}."""',
            "        session.add(obj)",
            "        await session.commit()",
            "        await session.refresh(obj)",
            "        return obj",
        ]

    def _generate_create_many(self, model: ParsedModel) -> list[str]:
        """Generate create_many method."""
        return [
            "    @staticmethod",
            f"    async def create_many(session: AsyncSession, objects: List[{model.name}]) -> List[{model.name}]:",
            f'        """Create multiple {model.name} records."""',
            "        session.add_all(objects)",
            "        await session.commit()",
            "        for obj in objects:",
            "            await session.refresh(obj)",
            "        return objects",
        ]

    def _generate_update(self, model: ParsedModel) -> list[str]:
        """Generate update method."""
        return [
            "    @staticmethod",
            "    async def update(",
            "        session: AsyncSession,",
            "        id: int,",
            "        data: dict,",
            f"    ) -> Optional[{model.name}]:",
            f'        """Update a {model.name} by ID."""',
            f"        obj = await {model.name}Repository.get_by_id(session, id)",
            "        if obj:",
            "            for key, value in data.items():",
            "                if value is not None and hasattr(obj, key):",
            "                    setattr(obj, key, value)",
            "            await session.commit()",
            "            await session.refresh(obj)",
            "        return obj",
        ]

    def _generate_delete(self, model: ParsedModel) -> list[str]:
        """Generate delete method."""
        return [
            "    @staticmethod",
            "    async def delete(session: AsyncSession, id: int) -> bool:",
            f'        """Delete a {model.name} by ID."""',
            f"        obj = await {model.name}Repository.get_by_id(session, id)",
            "        if obj:",
            "            await session.delete(obj)",
            "            await session.commit()",
            "            return True",
            "        return False",
        ]

    def _generate_exists(self, model: ParsedModel) -> list[str]:
        """Generate exists method."""
        return [
            "    @staticmethod",
            "    async def exists(session: AsyncSession, id: int) -> bool:",
            f'        """Check if {model.name} exists by ID."""',
            f"        statement = select({model.name}.id).where({model.name}.id == id)",
            "        result = await session.execute(statement)",
            "        return result.scalar() is not None",
        ]

    def _generate_count(self, model: ParsedModel) -> list[str]:
        """Generate count method."""
        return [
            "    @staticmethod",
            "    async def count(session: AsyncSession) -> int:",
            f'        """Count all {model.name} records."""',
            "        from sqlalchemy import func",
            f"        statement = select(func.count()).select_from({model.name})",
            "        result = await session.execute(statement)",
            "        return result.scalar() or 0",
        ]
