"""Group management service."""

import logging

from sqlalchemy import select
from sqlalchemy.exc import IntegrityError

from scribe.models import async_session
from scribe.models.group import Group, GroupMembership
from scribe.models.user import User

logger = logging.getLogger(__name__)


async def create_group(
    user_id: int, name: str, description: str | None = None
) -> Group:
    async with async_session() as session:
        group = Group(name=name, description=description, created_by=user_id)
        session.add(group)
        await session.flush()
        session.add(GroupMembership(group_id=group.id, user_id=user_id, role="owner"))
        await session.commit()
        await session.refresh(group)
        return group


async def list_groups(user_id: int) -> list[dict]:
    """All users see all groups with member count and their own membership status."""
    async with async_session() as session:
        groups = (await session.execute(select(Group).order_by(Group.name))).scalars().all()
        user_group_ids = set(
            (await session.execute(
                select(GroupMembership.group_id).where(GroupMembership.user_id == user_id)
            )).scalars().all()
        )
        result = []
        for g in groups:
            count = len(
                (await session.execute(
                    select(GroupMembership).where(GroupMembership.group_id == g.id)
                )).scalars().all()
            )
            d = g.to_dict()
            d["member_count"] = count
            d["is_member"] = g.id in user_group_ids
            result.append(d)
        return result


async def get_group(group_id: int) -> Group | None:
    async with async_session() as session:
        return await session.get(Group, group_id)


async def _is_group_owner(session, acting_user_id: int, group_id: int) -> bool:
    m = (await session.execute(
        select(GroupMembership).where(
            GroupMembership.group_id == group_id,
            GroupMembership.user_id == acting_user_id,
            GroupMembership.role == "owner",
        )
    )).scalar_one_or_none()
    return m is not None


async def update_group(
    acting_user_id: int, group_id: int, is_site_admin: bool, **fields
) -> Group | None:
    async with async_session() as session:
        group = await session.get(Group, group_id)
        if not group:
            return None
        if not is_site_admin and not await _is_group_owner(session, acting_user_id, group_id):
            return None
        for k, v in fields.items():
            if hasattr(group, k):
                setattr(group, k, v)
        await session.commit()
        await session.refresh(group)
        return group


async def delete_group(acting_user_id: int, group_id: int, is_site_admin: bool) -> bool:
    async with async_session() as session:
        group = await session.get(Group, group_id)
        if not group:
            return False
        if not is_site_admin and group.created_by != acting_user_id:
            return False
        await session.delete(group)
        await session.commit()
        return True


async def list_members(group_id: int) -> list[dict]:
    async with async_session() as session:
        rows = (await session.execute(
            select(GroupMembership, User)
            .join(User, User.id == GroupMembership.user_id)
            .where(GroupMembership.group_id == group_id)
        )).all()
        return [
            {**m.to_dict(), "username": u.username, "email": u.email}
            for m, u in rows
        ]


async def add_member(
    acting_user_id: int,
    group_id: int,
    target_user_id: int,
    role: str,
    is_site_admin: bool,
) -> GroupMembership | None:
    async with async_session() as session:
        if not is_site_admin and not await _is_group_owner(session, acting_user_id, group_id):
            return None
        membership = GroupMembership(group_id=group_id, user_id=target_user_id, role=role)
        session.add(membership)
        try:
            await session.commit()
        except IntegrityError:
            await session.rollback()
            return None
        await session.refresh(membership)
        return membership


async def update_member_role(
    acting_user_id: int,
    group_id: int,
    target_user_id: int,
    role: str,
    is_site_admin: bool,
) -> GroupMembership | None:
    async with async_session() as session:
        if not is_site_admin and not await _is_group_owner(session, acting_user_id, group_id):
            return None
        m = (await session.execute(
            select(GroupMembership).where(
                GroupMembership.group_id == group_id,
                GroupMembership.user_id == target_user_id,
            )
        )).scalar_one_or_none()
        if not m:
            return None
        m.role = role
        await session.commit()
        await session.refresh(m)
        return m


async def remove_member(
    acting_user_id: int,
    group_id: int,
    target_user_id: int,
    is_site_admin: bool,
) -> bool:
    """Group owner, site admin, or self-removal are all permitted."""
    async with async_session() as session:
        is_self = acting_user_id == target_user_id
        if not is_site_admin and not is_self:
            if not await _is_group_owner(session, acting_user_id, group_id):
                return False
        m = (await session.execute(
            select(GroupMembership).where(
                GroupMembership.group_id == group_id,
                GroupMembership.user_id == target_user_id,
            )
        )).scalar_one_or_none()
        if not m:
            return False
        await session.delete(m)
        await session.commit()
        return True


async def get_user_groups(user_id: int) -> list[Group]:
    async with async_session() as session:
        rows = (await session.execute(
            select(Group)
            .join(GroupMembership, GroupMembership.group_id == Group.id)
            .where(GroupMembership.user_id == user_id)
        )).scalars().all()
        return list(rows)
