"""Bulk tag operations over a set of images (Core, set-based, atomic)."""

from sqlalchemy import and_, func, select
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.ext.asyncio import AsyncSession

from ..models import Tag
from ..models.tag import image_tag
from ..models.tag_suggestion_rejection import TagSuggestionRejection


class BulkTagService:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def common_tags(self, image_ids: list[int]) -> list[dict]:
        """Tags present on EVERY image in image_ids."""
        if not image_ids:
            return []
        n = len(set(image_ids))
        stmt = (
            select(Tag.id, Tag.name, Tag.kind)
            .join(image_tag, image_tag.c.tag_id == Tag.id)
            .where(image_tag.c.image_record_id.in_(image_ids))
            .group_by(Tag.id, Tag.name, Tag.kind)
            .having(
                func.count(func.distinct(image_tag.c.image_record_id)) == n
            )
            .order_by(Tag.name.asc())
        )
        rows = (await self.session.execute(stmt)).all()
        return [
            {
                "id": r.id,
                "name": r.name,
                "kind": r.kind.value if hasattr(r.kind, "value") else r.kind,
            }
            for r in rows
        ]

    async def bulk_add(
        self, image_ids: list[int], tag_id: int, source: str = "manual"
    ) -> int:
        """Apply tag_id to every image. Idempotent per (image, tag).
        Returns the number of links actually created."""
        if not image_ids:
            return 0
        stmt = (
            pg_insert(image_tag)
            .values(
                [
                    {
                        "image_record_id": iid,
                        "tag_id": tag_id,
                        "source": source,
                    }
                    for iid in set(image_ids)
                ]
            )
            .on_conflict_do_nothing(
                index_elements=["image_record_id", "tag_id"]
            )
        )
        res = await self.session.execute(stmt)
        return res.rowcount or 0

    async def bulk_remove(self, image_ids: list[int], tag_id: int) -> int:
        """Remove tag_id from every image, and record a per-image rejection
        so the proactive allowlist sweep won't immediately re-add it
        (mirrors single-image removal semantics). Returns links removed."""
        if not image_ids:
            return 0
        res = await self.session.execute(
            image_tag.delete().where(
                and_(
                    image_tag.c.tag_id == tag_id,
                    image_tag.c.image_record_id.in_(image_ids),
                )
            )
        )
        await self.session.execute(
            pg_insert(TagSuggestionRejection)
            .values(
                [
                    {"image_record_id": iid, "tag_id": tag_id}
                    for iid in set(image_ids)
                ]
            )
            .on_conflict_do_nothing(
                index_elements=["image_record_id", "tag_id"]
            )
        )
        return res.rowcount or 0
