"""
Telegram client management using Telethon.
"""

import asyncio
import os
from typing import Optional, Dict, Any, List
from datetime import datetime

from telethon import TelegramClient
from telethon.errors import (
    FloodWaitError,
    SessionPasswordNeededError,
    PhoneCodeInvalidError,
)

from ..services.logger import get_logger
from ..models import Account, AccountStatus


class TelegramClientWrapper:
    """Wrapper for Telethon TelegramClient with additional functionality."""

    def __init__(self, account: Account, proxy: Optional[Dict[str, str]] = None):
        """Initialize client wrapper."""
        self.account = account
        self.proxy = proxy
        self.client: Optional[TelegramClient] = None
        self.logger = get_logger()
        self._connected = False
        self._authorized = False

    async def connect(self) -> bool:
        """Connect to Telegram."""
        try:
            if self.client and self._connected:
                return True

            # Create client
            self.client = TelegramClient(
                self.account.session_path,
                self.account.api_id,
                self.account.api_hash,
                proxy=self.proxy,
            )

            if self.proxy:
                self.logger.info(
                    f"Using proxy for account {self.account.id}: {self.proxy['proxy_type']}://{self.proxy['addr']}:{self.proxy['port']}"
                )

            # Connect
            await self.client.connect()
            self._connected = True

            # Check authorization
            self._authorized = await self.client.is_user_authorized()

            self.logger.log_telegram_event(
                "connect",
                self.account.id,
                f"Connected successfully. Authorized: {self._authorized}",
            )

            return True

        except Exception as e:
            self.logger.error(f"Failed to connect account {self.account.id}: {e}")
            self.account.status = AccountStatus.ERROR
            self.account.error_message = str(e)
            return False

    async def disconnect(self):
        """Disconnect from Telegram."""
        if self.client and self._connected:
            try:
                await self.client.disconnect()
                self._connected = False
                self._authorized = False
                self.logger.log_telegram_event("disconnect", self.account.id, "Disconnected")
            except Exception as e:
                self.logger.error(f"Error disconnecting account {self.account.id}: {e}")

    async def authorize(self, phone_code: str, password: Optional[str] = None) -> bool:
        """Authorize the client."""
        if not self.client or not self._connected:
            await self.connect()

        if not self.client:
            return False

        try:
            if not self._authorized:
                # Send code request
                await self.client.send_code_request(self.account.phone_number)

                # Sign in with code
                await self.client.sign_in(self.account.phone_number, phone_code)

                # Check if password is needed
                if not await self.client.is_user_authorized():
                    if password:
                        await self.client.sign_in(password=password)
                    else:
                        raise SessionPasswordNeededError("Two-step verification password required")

                self._authorized = True
                self.account.status = AccountStatus.ONLINE
                self.account.last_login = datetime.utcnow()
                self.account.error_message = None

                self.logger.log_telegram_event(
                    "authorize", self.account.id, "Authorization successful"
                )
                return True

            return True

        except PhoneCodeInvalidError:
            self.logger.error(f"Invalid phone code for account {self.account.id}")
            self.account.status = AccountStatus.ERROR
            self.account.error_message = "Invalid phone code"
            return False
        except SessionPasswordNeededError:
            self.logger.error(f"Password required for account {self.account.id}")
            self.account.status = AccountStatus.ERROR
            self.account.error_message = "Two-step verification password required"
            return False
        except Exception as e:
            self.logger.error(f"Authorization failed for account {self.account.id}: {e}")
            self.account.status = AccountStatus.ERROR
            self.account.error_message = str(e)
            return False

    async def send_message(
        self, peer: str, text: str, media_path: Optional[str] = None
    ) -> Dict[str, Any]:
        """Send a message."""
        if not self.client or not self._connected or not self._authorized:
            return {"success": False, "error": "Client not ready"}

        try:
            # Get entity
            entity = await self.client.get_entity(peer)

            # Send message
            if media_path:
                # Check if it's a URL or file path
                if media_path.startswith(("http://", "https://")):
                    # It's a URL - send as URL
                    message = await self.client.send_file(entity, media_path, caption=text)
                elif os.path.exists(media_path):
                    # It's a local file path
                    message = await self.client.send_file(entity, media_path, caption=text)
                else:
                    # Invalid media path
                    self.logger.warning(f"Media path does not exist: {media_path}")
                    message = await self.client.send_message(entity, text)
            else:
                message = await self.client.send_message(entity, text)

            self.logger.log_telegram_event(
                "send_message", self.account.id, f"Message sent to {peer}"
            )

            return {
                "success": True,
                "message_id": message.id,
                "chat_id": entity.id,
                "timestamp": datetime.utcnow(),
            }

        except FloodWaitError as e:
            self.logger.warning(f"Rate limited for account {self.account.id}: {e.seconds} seconds")
            return {"success": False, "error": "Rate limited", "retry_after": e.seconds}
        except Exception as e:
            self.logger.error(f"Failed to send message from account {self.account.id}: {e}")
            return {"success": False, "error": str(e)}

    async def get_me(self) -> Optional[Dict[str, Any]]:
        """Get current user info."""
        if not self.client or not self._connected or not self._authorized:
            return None

        try:
            me = await self.client.get_me()
            return {
                "id": me.id,
                "username": me.username,
                "first_name": me.first_name,
                "last_name": me.last_name,
                "phone": me.phone,
            }
        except Exception as e:
            self.logger.error(f"Failed to get user info for account {self.account.id}: {e}")
            return None

    def is_ready(self) -> bool:
        """Check if client is ready for sending."""
        return self._connected and self._authorized and self.client is not None

    def get_status(self) -> Dict[str, Any]:
        """Get client status."""
        return {
            "connected": self._connected,
            "authorized": self._authorized,
            "ready": self.is_ready(),
            "account_id": self.account.id,
            "phone": self.account.phone_number,
        }


class TelegramClientManager:
    """Manages multiple Telegram clients."""

    def __init__(self):
        """Initialize client manager."""
        self.clients: Dict[int, TelegramClientWrapper] = {}
        self.logger = get_logger()

    async def add_account(self, account: Account) -> bool:
        """Add an account to the manager."""
        try:
            # Get proxy config from account
            proxy = account.get_telethon_proxy()

            if proxy:
                self.logger.info(
                    f"Using proxy for account {account.id}: {proxy['proxy_type']}://{proxy['addr']}:{proxy['port']}"
                )

            # Create client wrapper
            client = TelegramClientWrapper(account, proxy)

            # Connect
            if await client.connect():
                self.clients[account.id] = client
                self.logger.log_telegram_event(
                    "add_account", account.id, "Account added successfully"
                )
                return True
            else:
                self.logger.error(f"Failed to add account {account.id}")
                return False

        except Exception as e:
            self.logger.error(f"Error adding account {account.id}: {e}")
            return False

    async def remove_account(self, account_id: int):
        """Remove an account from the manager."""
        if account_id in self.clients:
            await self.clients[account_id].disconnect()
            del self.clients[account_id]
            self.logger.log_telegram_event("remove_account", account_id, "Account removed")

    async def authorize_account(
        self, account_id: int, phone_code: str, password: Optional[str] = None
    ) -> bool:
        """Authorize an account."""
        if account_id not in self.clients:
            return False

        return await self.clients[account_id].authorize(phone_code, password)

    async def send_message(
        self, account_id: int, peer: str, text: str, media_path: Optional[str] = None
    ) -> Dict[str, Any]:
        """Send a message using an account."""
        if account_id not in self.clients:
            return {"success": False, "error": "Account not found"}

        return await self.clients[account_id].send_message(peer, text, media_path)

    def get_client(self, account_id: int) -> Optional[TelegramClientWrapper]:
        """Get a client by account ID."""
        return self.clients.get(account_id)

    def get_ready_clients(self) -> List[TelegramClientWrapper]:
        """Get all ready clients."""
        return [client for client in self.clients.values() if client.is_ready()]

    def get_client_status(self, account_id: int) -> Optional[Dict[str, Any]]:
        """Get client status."""
        if account_id in self.clients:
            return self.clients[account_id].get_status()
        return None

    def get_all_statuses(self) -> Dict[int, Dict[str, Any]]:
        """Get status of all clients."""
        return {account_id: client.get_status() for account_id, client in self.clients.items()}

    def get_event_loop(self):
        """Get the main event loop."""
        try:
            return asyncio.get_running_loop()
        except RuntimeError:
            return None

    async def disconnect_all(self):
        """Disconnect all clients."""
        for client in self.clients.values():
            await client.disconnect()
        self.clients.clear()
        self.logger.info("All clients disconnected")

    def get_account_count(self) -> int:
        """Get number of managed accounts."""
        return len(self.clients)

    def get_ready_count(self) -> int:
        """Get number of ready accounts."""
        return len(self.get_ready_clients())
