"""Multi-channel mandatory membership verification."""

from __future__ import annotations

import logging
from dataclasses import dataclass

from app.database.base import utcnow
from app.database.models import Post, RequiredChannel
from app.database.repositories import Repositories, effective_channels
from app.errors import MembershipUnavailableError, NotFoundError, ValidationError
from app.gateways.base import TelegramGateway
from app.utils.validation import validate_http_url

logger = logging.getLogger(__name__)


@dataclass(frozen=True, slots=True)
class ChannelCheck:
    channel: RequiredChannel
    joined: bool | None
    status: str
    error: str | None = None


@dataclass(frozen=True, slots=True)
class MembershipResult:
    checks: list[ChannelCheck]

    @property
    def unconfirmed(self) -> list[ChannelCheck]:
        return [item for item in self.checks if item.joined is not True]

    @property
    def all_joined(self) -> bool:
        return all(item.joined is True for item in self.checks) if self.checks else True

    @property
    def has_errors(self) -> bool:
        return any(item.error for item in self.checks)

    @property
    def error_count(self) -> int:
        return sum(1 for item in self.checks if item.error)


class MembershipService:
    def __init__(self, repos: Repositories, gateway: TelegramGateway) -> None:
        self.repos = repos
        self.gateway = gateway

    async def effective_for_post(self, post_id: int) -> list[RequiredChannel]:
        post = await self.repos.posts.get(post_id)
        if post is None:
            raise NotFoundError("پست پیدا نشد.")
        return await self.effective_for_post_model(post)

    async def effective_for_post_model(self, post: Post) -> list[RequiredChannel]:
        if not await self.repos.settings.get_bool("membership_enabled", True):
            return []
        channels = await self.repos.membership.list_required()
        overrides = await self.repos.membership.post_overrides(post.id)
        return effective_channels(post, channels, overrides)

    async def check_user(self, post_id: int, user_id: int) -> MembershipResult:
        channels = await self.effective_for_post(post_id)
        checks: list[ChannelCheck] = []
        for channel in channels:
            try:
                member = await self.gateway.get_chat_member(channel.chat_id, user_id)
                checks.append(
                    ChannelCheck(
                        channel=channel,
                        joined=member.joined,
                        status=member.status,
                    )
                )
            except Exception as exc:
                # An API failure is NEVER interpreted as "not joined" or "joined".
                logger.warning(
                    "membership check failed for required chat %s: %s",
                    channel.chat_id,
                    type(exc).__name__,
                )
                checks.append(
                    ChannelCheck(
                        channel=channel,
                        joined=None,
                        status="unknown",
                        error="بررسی عضویت در این کانال ممکن نشد.",
                    )
                )
        result = MembershipResult(checks)
        await self.repos.stats.record_membership_check(
            user_id, post_id, result.all_joined, result.error_count
        )
        return result

    async def check_bot_access(self, channel: RequiredChannel) -> bool:
        me = await self.gateway.get_me()
        try:
            member = await self.gateway.get_chat_member(channel.chat_id, me.id)
            ok = member.status in {"creator", "administrator"}
            await self.repos.membership.update_required(
                channel.id,
                bot_status=member.status,
                last_checked_at=utcnow(),
                last_error=None if ok else "ربات در این کانال ادمین نیست.",
            )
            return ok
        except Exception as exc:
            await self.repos.membership.update_required(
                channel.id,
                bot_status="error",
                last_checked_at=utcnow(),
                last_error="بررسی دسترسی ربات ممکن نشد.",
            )
            logger.warning("bot access check failed for %s: %s", channel.chat_id, type(exc).__name__)
            return False

    async def add_channel(
        self,
        chat_id: int,
        *,
        title: str | None,
        invite_link: str | None,
        actor_id: int,
    ) -> RequiredChannel:
        try:
            chat = await self.gateway.get_chat(chat_id)
        except Exception as exc:
            raise MembershipUnavailableError(
                "اطلاعات کانال خوانده نشد. ربات باید عضو/ادمین کانال باشد."
            ) from exc
        if chat.chat_type not in {"channel", "group", "supergroup"}:
            raise ValidationError("فقط کانال یا گروه می‌تواند الزام عضویت باشد.")
        link = (invite_link or "").strip()
        if not link and chat.username:
            link = f"https://t.me/{chat.username}"
        if not link:
            raise ValidationError(
                "برای کانال خصوصی، لینک عضویت را وارد کنید (مثلاً https://t.me/+...)."
            )
        link = validate_http_url(link)
        me = await self.gateway.get_me()
        try:
            bot_member = await self.gateway.get_chat_member(chat_id, me.id)
        except Exception as exc:
            raise MembershipUnavailableError(
                "دسترسی ربات برای بررسی عضویت تأیید نشد. ربات را ادمین کانال کنید."
            ) from exc
        if bot_member.status not in {"creator", "administrator"}:
            raise MembershipUnavailableError(
                "برای بررسی عضویت کاربران، ربات باید در کانال الزامی ادمین باشد."
            )
        channel, _created = await self.repos.membership.add_required(
            chat_id=chat_id,
            title=title or chat.title or str(chat_id),
            invite_link=link,
            chat_type=chat.chat_type,
            bot_status=bot_member.status,
        )
        await self.repos.audit.log(
            actor_id,
            "required_channel.add",
            entity_type="required_channel",
            entity_id=chat_id,
        )
        return channel

    async def refresh_all(self) -> dict[int, bool]:
        result: dict[int, bool] = {}
        for channel in await self.repos.membership.list_required(active_only=True):
            result[channel.id] = await self.check_bot_access(channel)
        return result
