import sys
import asyncio
import logging

from pathlib import Path
from telethon.sync import TelegramClient, types, functions
from telethon.sessions.string import StringSession

from telethon.tl.functions.contacts import BlockRequest
from telethon.tl.functions.messages import DeleteHistoryRequest

from configparser import ConfigParser
from pyutilities import SESSION, PROXY
from pyutilities.singlefunc import chunk_generate, get_sessions

logging.getLogger("telethon").setLevel(logging.CRITICAL)

from loguru import logger

config = ConfigParser()
config.read('config.ini')


logger.remove()  # Remove default handler to avoid duplicate logs
logger.add('error.log', rotation='10 MB', retention='7 days', level='ERROR')

logger.add(
    sys.stdout,  # Output to standard output
    colorize=True,
    format="<yellow>{time:YYYY-MM-DD HH:mm:ss}</yellow> "
           "<level>{level}</level> | "
           "<MAGENTA> {name} : {line} </MAGENTA> - <level>{message}</level>",
)

def move_session(session: Path, new_path:str) -> None:
    ''' Move Session '''
    path = Path('sessions')
    new: Path = path.joinpath(new_path)

    if not new.exists():
        new.mkdir(parents=True, exist_ok=True)
    
    session.rename(new.joinpath(session.name))
    
    js_file = session.with_suffix('.json')
    if js_file.exists():
        js_file.rename(new.joinpath(js_file.name))


async def delete_chats(session_name: str, client: TelegramClient):
    async with client:
        # Get the participants in the chat
        dialogs = await client.get_dialogs()

        for dialog in dialogs:
            entity = dialog.entity
            
            #if isinstance(entity, types.User) and (entity.id == 777000 or str(entity.username).lower() == "telegram"):
             #   logger.info(f"{session_name} | Skipped official Telegram chat")
             #   continue

            if getattr(entity, 'bot', False):  # Check if the entity is a bot
                logger.info(f"Bot found in dialog: {entity.username or entity.id}")
                

                # Delete chat history
                await client(DeleteHistoryRequest(peer=entity.id, max_id=0, just_clear=False, revoke=True))
                
                # Block the bot
                await client(BlockRequest(entity.id))
                logger.info(f"{session_name} | Blocked bot: {entity.username or entity.id}")

            elif isinstance(entity, types.User):
                # Delete chat history
                await client(DeleteHistoryRequest(peer=entity.id, max_id=0, just_clear=False, revoke=True))
                logger.info(f"{session_name} | delete user {entity.first_name} chats: @{entity.username or entity.id}")



async def leaveChannel(session: Path, retry: int = 3):
    ''' Leave Channel '''

    sessioncli = SESSION(session)
    proxy: dict = PROXY.getProxy()

    client: TelegramClient = sessioncli.get_client(
        connection_retries=1,
        timeout=5
        )
    
    client.set_proxy(proxy=proxy)

    limit_leave = config['channel']['left']

    if limit_leave.isdigit():
        limit_leave = int(limit_leave)
    
    elif limit_leave.upper() == 'ALL':
        pass

    else:
        logger.error(f'invalid value for config -> channel -> left : {limit_leave}')
        return None


    try:
        await asyncio.wait_for(client.connect(), timeout=10)

        if await client.is_user_authorized():
            me = await client.get_me()

            count = 0
            async for dialog in client.iter_dialogs():
                if isinstance(dialog.entity, types.Channel):
                    if limit_leave == 'ALL':
                        count += 1

                    elif count >= limit_leave:
                        break

                    result = await client(functions.channels.LeaveChannelRequest(
                         channel=dialog.entity
                         ))
                    
                    count += 1
                    logger.info(f'{session.name} | [{count}] leave channel - {dialog.title}')
            


            await delete_chats(session.name, client)

            await client.disconnect()
            await asyncio.sleep(1)

            await asyncio.to_thread(move_session, session, 'OK')


            
        else:
            await client.disconnect()
            await asyncio.sleep(1)
            await asyncio.to_thread(move_session, session, 'invalid')
            await asyncio.sleep(1)

            logger.error(f'Invaild Session {session}')      
        

    except (
        asyncio.TimeoutError,
        asyncio.CancelledError,
        ConnectionError,
        TypeError,
        asyncio.IncompleteReadError,
    ) as e:        
        await client.disconnect()
        await asyncio.sleep(1)
        
        addr: str = client._proxy.get('addr')
        if retry <= 0:
            logger.error(f'{session.name} | {addr} | Connection Timeout...')
            return None
        
        logger.warning(f'{session.name} | {addr} | Connection Error : {e} | retrys ({retry})...')
        res = await leaveChannel(session, retry - 1)
        return res

    except Exception as e:
        logger.error(f'{session.name} | Error : {e}')

        await client.disconnect()
        return None



async def main():
    ''' Main '''
    chunk_sessions = chunk_generate(sessions, int(config['setting']['chunk']))
    for session_chunk in chunk_sessions:
        task = asyncio.gather(*(leaveChannel(session) for session in session_chunk))
        await task
        await asyncio.sleep(int(config['setting']['chunk_sleep']))
        logger.info(f'Sleep {config["setting"]["chunk_sleep"]}s')

                                
if __name__ == "__main__":
    try:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)

        PROXY.load()
        logger.info(f':: Total Proxys [{len(PROXY.proxys)}]')
        sessions = get_sessions(Path('sessions'))
        logger.info(f'Total Sessions: {len(sessions)}')
        
        loop.run_until_complete(main())


    except KeyboardInterrupt:
        logger.warning("Stop By User!")