"""
actions/join_leave.py
Full logging integrated
"""

import re
from typing import List, Union

from pyrogram.errors import (
    FloodWait,
    RPCError,
    UserAlreadyParticipant,
    InviteHashInvalid,
    InviteHashExpired,
    ChannelPrivate
)

from core.safe_executor import SafeExecutor
from core.delay_engine import DelayEngine
from core.flood_handler import FloodHandler
from core.logger import get_action_logger


INVITE_REGEX = re.compile(
    r"(?:https?://)?t\.me/(?:\+|joinchat/)([\w-]+)"
)

PUBLIC_REGEX = re.compile(
    r"(?:https?://)?t\.me/([\w_]+)"
)


class JoinLeaveManager:

    def __init__(self, client, session_name: str):

        self.client = client
        self.session_name = session_name

        self.logger = get_action_logger(
            action="join_leave",
            session=session_name
        )

    # --------------------------------------------------
    # PARSE
    # --------------------------------------------------

    def _parse_chat(self, chat):

        invite_match = INVITE_REGEX.search(str(chat))
        if invite_match:
            return "invite", invite_match.group(1)

        public_match = PUBLIC_REGEX.search(str(chat))
        if public_match:
            return "public", public_match.group(1)

        if isinstance(chat, str):
            return "public", chat.replace("@", "")

        return "id", chat

    # --------------------------------------------------
    # JOIN EXEC
    # --------------------------------------------------

    async def _join_exec(self, chat):

        chat_type, value = self._parse_chat(chat)

        if chat_type == "invite":
            return await self.client.join_chat(
                f"https://t.me/+{value}"
            )

        return await self.client.join_chat(value)

    # --------------------------------------------------
    # LEAVE EXEC
    # --------------------------------------------------

    async def _leave_exec(self, chat):
        return await self.client.leave_chat(chat)

    # --------------------------------------------------
    # NORMALIZE
    # --------------------------------------------------

    def _normalize(
        self,
        chats: Union[str, int, List]
    ) -> List:

        if isinstance(chats, (str, int)):
            return [chats]

        return list(chats)

    # --------------------------------------------------
    # JOIN
    # --------------------------------------------------

    async def join(
        self,
        chats,
        use_delay=True
    ):

        chats = self._normalize(chats)

        result = {"success": 0, "failed": 0}

        for chat in chats:

            try:

                self.logger.info(f"Joining → {chat}")

                if use_delay:
                    await DelayEngine.join_delay()

                await SafeExecutor.run(
                    self._join_exec(chat),
                    session_name=self.session_name,
                    action_name="join_chat"
                )

                self.logger.info(f"Joined → {chat}")
                result["success"] += 1

            except UserAlreadyParticipant:

                self.logger.warning(
                    f"Already joined → {chat}"
                )
                result["success"] += 1

            except FloodWait as e:

                self.logger.warning(
                    f"FloodWait {e.value}s → {chat}"
                )

                await FloodHandler.handle(
                    e,
                    session_name=self.session_name,
                    action="join_chat"
                )

                result["failed"] += 1

            except (
                InviteHashInvalid,
                InviteHashExpired,
                ChannelPrivate
            ):

                self.logger.error(
                    f"Invalid invite → {chat}"
                )
                result["failed"] += 1

            except RPCError as e:

                self.logger.error(
                    f"RPC Error → {e}"
                )
                result["failed"] += 1

            except Exception as e:

                self.logger.exception(
                    f"Join error → {e}"
                )
                result["failed"] += 1

        self.logger.info(
            f"Join result → {result}"
        )

        return result

    # --------------------------------------------------
    # LEAVE
    # --------------------------------------------------

    async def leave(
        self,
        chats,
        use_delay=True
    ):

        chats = self._normalize(chats)

        result = {"success": 0, "failed": 0}

        for chat in chats:

            try:

                self.logger.info(f"Leaving → {chat}")

                if use_delay:
                    await DelayEngine.leave_delay()

                await SafeExecutor.run(
                    self._leave_exec(chat),
                    session_name=self.session_name,
                    action_name="leave_chat"
                )

                self.logger.info(f"Left → {chat}")
                result["success"] += 1

            except FloodWait as e:

                self.logger.warning(
                    f"FloodWait {e.value}s → leave"
                )

                await FloodHandler.handle(
                    e,
                    session_name=self.session_name,
                    action="leave_chat"
                )

                result["failed"] += 1

            except RPCError as e:

                self.logger.error(
                    f"RPC Error → {e}"
                )
                result["failed"] += 1

            except Exception as e:

                self.logger.exception(
                    f"Leave error → {e}"
                )
                result["failed"] += 1

        self.logger.info(
            f"Leave result → {result}"
        )

        return result
