"""Base service for {{ model_name }}.

⚠️ AUTO-GENERATED BY PRISM - DO NOT EDIT
Extend this class in services/{{ snake_name }}.py to add custom logic.
"""

from __future__ import annotations

from typing import Any, Sequence

from sqlalchemy import select, func, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from .base import ServiceBase
from {{ package_name }}.models.{{ snake_name }} import {{ model_name }}
from {{ package_name }}.schemas.{{ snake_name }} import (
    {{ model_name }}Create,
    {{ model_name }}Update,
    {{ model_name }}Filter,
)
{% for rel in relationships %}
from {{ package_name }}.models.{{ rel.target_snake }} import {{ rel.target_model }}
{% endfor %}


class {{ model_name }}ServiceBase(ServiceBase[{{ model_name }}, {{ model_name }}Create, {{ model_name }}Update]):
    """Generated base service for {{ model_name }}.

    Extend this class in services/{{ snake_name }}.py to add custom logic.
    """

    model = {{ model_name }}

    async def list(
        self,
        *,
        skip: int = 0,
        limit: int = 100,
        filters: {{ model_name }}Filter | None = None,
        sort_by: str | None = None,
        sort_order: str = "asc",
        include_deleted: bool = False,
        load_relationships: list[str] | None = None,
    ) -> Sequence[{{ model_name }}]:
        """List {{ model_name }} records with filtering and sorting.

        Args:
            skip: Number of records to skip.
            limit: Maximum number of records to return.
            filters: Filter parameters.
            sort_by: Field to sort by.
            sort_order: Sort order ('asc' or 'desc').
            include_deleted: Whether to include soft-deleted records.
            load_relationships: List of relationship names to eagerly load.

        Returns:
            List of {{ model_name }} records.
        """
        query = select(self.model)

        # Apply soft delete filter
        if hasattr(self.model, "deleted_at") and not include_deleted:
            query = query.where(self.model.deleted_at.is_(None))

        # Apply filters
        if filters:
            query = self._apply_filters(query, filters)

        # Eagerly load specified relationships
        if load_relationships:
            for rel_name in load_relationships:
                if hasattr(self.model, rel_name):
                    query = query.options(selectinload(getattr(self.model, rel_name)))

        # Apply sorting
        if sort_by and hasattr(self.model, sort_by):
            column = getattr(self.model, sort_by)
            if sort_order.lower() == "desc":
                column = column.desc()
            query = query.order_by(column)

        # Apply pagination
        query = query.offset(skip).limit(limit)

        result = await self.db.execute(query)
        return result.scalars().all()

    async def count_filtered(
        self,
        *,
        filters: {{ model_name }}Filter | None = None,
        include_deleted: bool = False,
    ) -> int:
        """Count records matching filters.

        Args:
            filters: Filter parameters.
            include_deleted: Whether to include soft-deleted records.

        Returns:
            Count of matching records.
        """
        query = select(func.count()).select_from(self.model)

        if hasattr(self.model, "deleted_at") and not include_deleted:
            query = query.where(self.model.deleted_at.is_(None))

        if filters:
            query = self._apply_filters(query, filters)

        result = await self.db.execute(query)
        return result.scalar_one()

    def _apply_filters(
        self,
        query: Any,
        filters: {{ model_name }}Filter,
    ) -> Any:
        """Apply filters to a query.

        Args:
            query: The SQLAlchemy query.
            filters: The filter parameters.

        Returns:
            The filtered query.
        """
        filter_data = filters.model_dump(exclude_unset=True, exclude_none=True)
{% if relationships %}

        # Track joined relationships to avoid duplicate joins
        joined_relationships: set[str] = set()
{% endif %}

        for field_name, value in filter_data.items():
{% for rel in relationships %}
            # Handle {{ rel.name }} relationship filters
            if field_name == "{{ rel.name }}_id":
                if "{{ rel.name }}" not in joined_relationships:
                    query = query.join(self.model.{{ rel.name }})
                    joined_relationships.add("{{ rel.name }}")
                query = query.where({{ rel.target_model }}.id == value)
                continue
            if field_name == "{{ rel.name }}_ids":
                if "{{ rel.name }}" not in joined_relationships:
                    query = query.join(self.model.{{ rel.name }})
                    joined_relationships.add("{{ rel.name }}")
                query = query.where({{ rel.target_model }}.id.in_(value))
                continue
{% endfor %}
            # Handle special filter suffixes
            if field_name.endswith("_ne"):
                base_field = field_name[:-3]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field) != value)
            elif field_name.endswith("_gt"):
                base_field = field_name[:-3]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field) > value)
            elif field_name.endswith("_gte"):
                base_field = field_name[:-4]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field) >= value)
            elif field_name.endswith("_lt"):
                base_field = field_name[:-3]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field) < value)
            elif field_name.endswith("_lte"):
                base_field = field_name[:-4]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field) <= value)
            elif field_name.endswith("_like"):
                base_field = field_name[:-5]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field).like(value))
            elif field_name.endswith("_ilike"):
                base_field = field_name[:-6]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field).ilike(value))
            elif field_name.endswith("_in"):
                base_field = field_name[:-3]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field).in_(value))
            elif field_name.endswith("_not_in"):
                base_field = field_name[:-7]
                if hasattr(self.model, base_field):
                    query = query.where(~getattr(self.model, base_field).in_(value))
            elif field_name.endswith("_is_null"):
                base_field = field_name[:-8]
                if hasattr(self.model, base_field):
                    if value:
                        query = query.where(getattr(self.model, base_field).is_(None))
                    else:
                        query = query.where(getattr(self.model, base_field).isnot(None))
            elif field_name.endswith("_contains"):
                base_field = field_name[:-9]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field).contains(value))
            elif field_name.endswith("_starts_with"):
                base_field = field_name[:-12]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field).startswith(value))
            elif field_name.endswith("_ends_with"):
                base_field = field_name[:-10]
                if hasattr(self.model, base_field):
                    query = query.where(getattr(self.model, base_field).endswith(value))
            elif hasattr(self.model, field_name):
                # Exact match
                query = query.where(getattr(self.model, field_name) == value)

        return query
{% if create_with_nested_method %}
{{ create_with_nested_method }}
{%- endif %}
{% if temporal_methods %}
{{ temporal_methods }}
{%- endif %}
{% for rel in m2m_relationships %}

    async def add_{{ rel.name }}(
        self,
        id: int,
        related_ids: list[int],
    ) -> {{ model_name }} | None:
        """Add {{ rel.target_model }} entities to the {{ rel.name }} relationship.

        Args:
            id: The {{ model_name }} ID.
            related_ids: List of {{ rel.target_model }} IDs to add.

        Returns:
            The updated {{ model_name }} or None if not found.
        """
        # Eagerly load the relationship to avoid lazy loading issues in async
        db_obj = await self.get(id, load_relationships=["{{ rel.name }}"])
        if db_obj is None:
            return None

        if not related_ids:
            return db_obj

        # Fetch related entities
        result = await self.db.execute(
            select({{ rel.target_model }}).where({{ rel.target_model }}.id.in_(related_ids))
        )
        related_entities = result.scalars().all()

        # Add only entities not already in the relationship
        existing_ids = {item.id for item in db_obj.{{ rel.name }}}
        for entity in related_entities:
            if entity.id not in existing_ids:
                db_obj.{{ rel.name }}.append(entity)

        await self.db.commit()
        await self.db.refresh(db_obj)
        return db_obj

    async def remove_{{ rel.name }}(
        self,
        id: int,
        related_ids: list[int],
    ) -> {{ model_name }} | None:
        """Remove {{ rel.target_model }} entities from the {{ rel.name }} relationship.

        Args:
            id: The {{ model_name }} ID.
            related_ids: List of {{ rel.target_model }} IDs to remove.

        Returns:
            The updated {{ model_name }} or None if not found.
        """
        # Eagerly load the relationship to avoid lazy loading issues in async
        db_obj = await self.get(id, load_relationships=["{{ rel.name }}"])
        if db_obj is None:
            return None

        if not related_ids:
            return db_obj

        # Remove entities with matching IDs
        related_ids_set = set(related_ids)
        db_obj.{{ rel.name }} = [
            item for item in db_obj.{{ rel.name }}
            if item.id not in related_ids_set
        ]

        await self.db.commit()
        await self.db.refresh(db_obj)
        return db_obj

    async def set_{{ rel.name }}(
        self,
        id: int,
        related_ids: list[int],
    ) -> {{ model_name }} | None:
        """Replace all {{ rel.target_model }} entities in the {{ rel.name }} relationship.

        Args:
            id: The {{ model_name }} ID.
            related_ids: List of {{ rel.target_model }} IDs to set.

        Returns:
            The updated {{ model_name }} or None if not found.
        """
        # Eagerly load the relationship to avoid lazy loading issues in async
        db_obj = await self.get(id, load_relationships=["{{ rel.name }}"])
        if db_obj is None:
            return None

        if not related_ids:
            db_obj.{{ rel.name }} = []
        else:
            # Fetch all related entities
            result = await self.db.execute(
                select({{ rel.target_model }}).where({{ rel.target_model }}.id.in_(related_ids))
            )
            db_obj.{{ rel.name }} = list(result.scalars().all())

        await self.db.commit()
        await self.db.refresh(db_obj)
        return db_obj
{% endfor %}


__all__ = ["{{ model_name }}ServiceBase"]
