"""Файл с кастомными виджетами приложения"""

from textual.containers import Horizontal, Vertical, Container, VerticalScroll
from textual.widget import Widget
from textual.reactive import Reactive
from textual.widgets import Input, Button, Label, Static, ContentSwitcher
from textual.app import ComposeResult, RenderResult
from textual.keys import Keys
from telethon import TelegramClient, events, utils
import datetime
import unicodedata
import re
import emoji
import os
import tempfile
from PIL import Image
import pywhatkit as kit
from textual import log

def remove_emoji(text: str) -> str:
    """Удаляет эмодзи из текста"""
    if not text:
        return ""
    return emoji.replace_emoji(text, '')

def normalize_text(text: str) -> str:
    """Нормализует текст для корректного отображения"""
    if not text:
        return ""
    
    try:
        # Преобразуем в строку, если это не строка
        text = str(text)
        
        # Удаляем эмодзи
        text = remove_emoji(text)
        
        # Нормализуем Unicode
        text = unicodedata.normalize('NFKC', text)
        
        # Заменяем специальные символы на их ASCII-эквиваленты
        text = text.replace('—', '-').replace('–', '-').replace('…', '...')
        
        # Удаляем все управляющие символы, кроме новой строки и табуляции
        text = ''.join(char for char in text if unicodedata.category(char)[0] != 'C' 
                      or char in ('\n', '\t'))
        
        # Удаляем множественные пробелы
        text = ' '.join(text.split())
        
        return text
    except Exception as e:
        log(f"Ошибка нормализации текста: {e}")
        return "Ошибка отображения"

def safe_ascii(text: str) -> str:
    """Преобразует текст в безопасный ASCII-формат"""
    if not text:
        return ""
    # Удаляем эмодзи
    text = remove_emoji(text)
    # Оставляем только ASCII символы и пробелы
    return ''.join(char for char in text if ord(char) < 128 or char.isspace())

def convert_image_to_ascii(image_path: str, width: int = 50) -> str:
    """Конвертирует изображение в ASCII-арт"""
    try:
        # Открываем изображение
        img = Image.open(image_path)
        
        # Конвертируем в RGB если нужно
        if img.mode != 'RGB':
            img = img.convert('RGB')
        
        # Изменяем размер, сохраняя пропорции
        aspect_ratio = img.height / img.width
        height = int(width * aspect_ratio * 0.5)  # * 0.5 потому что символы выше чем шире
        img = img.resize((width, height))
        
        # Конвертируем в ASCII
        ascii_str = kit.image_to_ascii_art(image_path, output_file=None)
        
        # Очищаем временный файл
        os.remove(image_path)
        
        return ascii_str
    except Exception as e:
        log(f"Ошибка конвертации изображения: {e}")
        return "Ошибка загрузки изображения"

class Chat(Static):
    """Класс виджета чата для панели чатов"""

    DEFAULT_CSS = """
    Chat {
        width: 100%;
        height: auto;
        min-height: 3;
        padding: 1 2;
        border: solid $accent;
        margin: 1 0;
        background: $surface;
    }
    Chat:hover {
        background: $accent 20%;
    }
    Chat.-selected {
        background: $accent 30%;
    }
    Chat:focus {
        background: $accent 40%;
        border: double $accent;
    }
    .chat-avatar {
        width: 3;
        height: 3;
        content-align: center middle;
        border: solid $accent;
        margin-right: 1;
        background: $boost;
    }
    .chat-content {
        width: 100%;
        height: auto;
    }
    .chat-name {
        width: 100%;
        color: $text;
        text-style: bold;
    }
    .chat-message {
        width: 100%;
        color: $text-muted;
    }
    """

    def __init__(
            self, 
            name: str | None = None, 
            id: str | None = None, 
            classes: str | None = None, 
            disabled: bool = False
    ) -> None:
        super().__init__(name=name, id=id, classes=classes, disabled=disabled)
        self.can_focus = True
        self._username = ""
        self._msg = ""
        self._peer_id = 0
        self._is_selected = False
        self._folder = 0

    def on_mount(self) -> None:
        self.switcher = self.screen.query_one("#dialog_switcher", ContentSwitcher)

    @property
    def username(self) -> str:
        return self._username

    @username.setter
    def username(self, value: str) -> None:
        self._username = value
        self.refresh()

    @property
    def msg(self) -> str:
        return self._msg

    @msg.setter
    def msg(self, value: str) -> None:
        self._msg = value
        self.refresh()

    @property
    def peer_id(self) -> int:
        return self._peer_id

    @peer_id.setter
    def peer_id(self, value: int) -> None:
        self._peer_id = value

    @property
    def is_selected(self) -> bool:
        return self._is_selected

    @is_selected.setter
    def is_selected(self, value: bool) -> None:
        self._is_selected = value
        self.set_class(value, "-selected")

    @property
    def folder(self) -> int:
        return self._folder

    @folder.setter
    def folder(self, value: int) -> None:
        self._folder = value
        self.refresh()

    def on_focus(self) -> None:
        # Снимаем выделение со всех чатов
        for chat in self.screen.query(Chat):
            chat.is_selected = False
        
        # Выделяем текущий чат
        self.is_selected = True
        
        # Прокручиваем к этому чату
        self.screen.chat_container.scroll_to(self, animate=False)

    def on_click(self) -> None:
        self.focus()
        
        dialog_id = f"dialog-{str(self.peer_id)}"
        try:
            self.switcher.mount(Dialog(
                telegram_client=self.app.telegram_client, 
                chat_id=self.peer_id, 
                id=dialog_id
            ))
        except Exception as e:
            log(f"Ошибка открытия диалога: {e}")
            return
            
        self.switcher.current = dialog_id

    def on_key(self, event: Keys) -> None:
        if event.key == "enter":
            self.on_click()
        elif event.key == "up" and self.id != "chat-1":
            # Фокусируемся на предыдущем чате
            prev_chat = self.screen.chat_container.query_one(f"#chat-{int(self.id.split('-')[1]) - 1}")
            if prev_chat:
                prev_chat.focus()
        elif event.key == "down":
            # Фокусируемся на следующем чате
            next_chat = self.screen.chat_container.query_one(f"#chat-{int(self.id.split('-')[1]) + 1}")
            if next_chat:
                next_chat.focus()

    def compose(self) -> ComposeResult:
        """Компонуем виджет чата"""
        try:
            # Подготавливаем данные
            name = normalize_text(self._username)
            if not name:
                name = "Без названия"
            
            msg = normalize_text(self._msg)
            if not msg:
                msg = "Нет сообщений"
            elif len(msg) > 50:
                msg = msg[:47] + "..."
            
            # Добавляем метку папки если нужно
            if self._folder == 1:
                name += " [Архив]"
            
            # Получаем первую букву для аватара (используем только видимые символы)
            first_letter = next((c for c in name if c.isprintable()), "?")
            
            # Создаем виджеты
            with Horizontal():
                yield Static(first_letter, classes="chat-avatar")
                with Vertical(classes="chat-content"):
                    yield Static(name, classes="chat-name")
                    yield Static(msg, classes="chat-message")
                    
        except Exception as e:
            log(f"Ошибка отображения чата: {e}")
            # Показываем запасной вариант в случае ошибки
            with Horizontal():
                yield Static("?", classes="chat-avatar")
                with Vertical(classes="chat-content"):
                    yield Static("Ошибка отображения", classes="chat-name")
                    yield Static("Попробуйте обновить список", classes="chat-message")

class Dialog(Widget):
    """Класс окна диалога"""
    
    def __init__(
            self, 
            id=None, 
            classes=None, 
            disabled=None, 
            telegram_client: TelegramClient | None = None,
            chat_id = None
        ) -> None:
        super().__init__(id=id, classes=classes, disabled=disabled)
        self.telegram_client = telegram_client
        self.chat_id = chat_id
        self.is_msg_update_blocked = False
        self.messages_loaded = 0
        self.is_loading = False

    async def on_mount(self) -> None:
        self.limit = 50  # Увеличиваем начальное количество сообщений
        self.messages_loaded = self.limit

        self.msg_input = self.query_one("#msg_input")
        self.dialog = self.query_one(Vertical).query_one("#dialog")
        self.load_more_btn = self.query_one("#load_more")

        self.me = await self.telegram_client.get_me()

        self.dialog.scroll_end(animate=False)
        await self.update_dialog()

        for event in (
            events.NewMessage, 
            events.MessageDeleted, 
            events.MessageEdited
        ):
            self.telegram_client.on(
                event(chats=(self.chat_id))
            )(self.update_dialog)

    def mount_messages(self, limit: int) -> None:
        print("Загрузка виджетов сообщений...")

        msg_amount = len(self.dialog.query(Message))

        if limit > msg_amount:
            for i in range(limit - msg_amount):
                self.dialog.mount(
                    Message(id=f"msg-{i + msg_amount + 1}"), 
                    before=0
                )
        elif limit < msg_amount:
            for i in range(msg_amount - limit):
                self.dialog.query(Message).last().remove()

    async def update_dialog(self, event = None) -> None:
        log("Запрос обновления сообщений")

        if not self.is_msg_update_blocked:
            self.is_msg_update_blocked = True

            messages = await self.telegram_client.get_messages(
                entity=self.chat_id, limit=self.limit
            )
            log("Получены сообщения")

            limit = len(messages)
            self.mount_messages(limit)

            for i in range(limit):
                msg = self.dialog.query_one(f"#msg-{i + 1}")
                msg.message = ""
                
                # Обрабатываем изображения
                if messages[i].media:
                    try:
                        # Скачиваем изображение
                        image_data = await self.telegram_client.download_media(
                            messages[i].media, 
                            bytes
                        )
                        if image_data:
                            await msg.set_image(image_data)
                    except Exception as e:
                        log(f"Ошибка загрузки изображения: {e}")
                
                # Обрабатываем текст
                if str(messages[i].message):
                    msg.message = normalize_text(str(messages[i].message))
                
                try:
                    is_me = messages[i].from_id.user_id == self.me.id
                except:
                    is_me = False
                
                msg.is_me = is_me
                msg.username = normalize_text(utils.get_display_name(messages[i].sender))
                msg.send_time = messages[i]\
                    .date\
                    .astimezone(datetime.timezone.utc)\
                    .strftime("%H:%M")

            self.is_msg_update_blocked = False
            log("Сообщения обновлены")
        else:
            log("Обновление сообщений невозможно: уже выполняется")

    async def load_more_messages(self) -> None:
        if not self.is_loading:
            self.is_loading = True
            self.load_more_btn.disabled = True
            self.load_more_btn.label = "Загрузка..."
            
            try:
                messages = await self.telegram_client.get_messages(
                    entity=self.chat_id,
                    limit=self.limit,
                    offset_id=self.messages_loaded
                )
                
                if messages:
                    self.messages_loaded += len(messages)
                    self.mount_messages(self.messages_loaded)
                    await self.update_dialog()
            finally:
                self.is_loading = False
                self.load_more_btn.disabled = False
                self.load_more_btn.label = "Загрузить еще"

    def compose(self) -> ComposeResult:
        with Vertical():
            yield Button("Загрузить еще", id="load_more", variant="default")
            yield VerticalScroll(id="dialog")
            with Horizontal(id="input_place"):
                yield Input(placeholder="Сообщение", id="msg_input")
                yield Button(label=">", id="send", variant="primary")

    async def on_button_pressed(self, event) -> None:
        if event.button.id == "load_more":
            await self.load_more_messages()
        else:
            await self.send_message()
    
    async def on_input_submitted(self, event = None) -> None:
        await self.send_message()

    async def send_message(self) -> None:
        try:
            await self.telegram_client.send_message(
                self.chat_id, 
                normalize_text(str(self.msg_input.value))
            )
        except ValueError:
            self.app.notify("Ошибка отправки")
        self.msg_input.value = ""
        await self.update_dialog()

class Message(Widget):
    """Класс виджета сообщений для окна диалога"""

    message: Reactive[str] = Reactive("", recompose=True)
    is_me: Reactive[bool] = Reactive(False, recompose=True)
    username: Reactive[str] = Reactive("", recompose=True)
    send_time: Reactive[str] = Reactive("", recompose=True)
    
    def __init__(
            self, 
            name=None, 
            id=None, 
            classes=None, 
            disabled=False
    ) -> None:
        super().__init__(name=name, id=id, classes=classes, disabled=disabled)

    def on_mount(self) -> None:
        pass

    def compose(self) -> ComposeResult:
        static = Static(normalize_text(self.message))
        static.border_title = normalize_text(self.username) * (not self.is_me)
        static.border_subtitle = self.send_time
        
        with Container():
            yield static
        
        if self.is_me:
            self.classes = "is_me_true"
        else:
            self.classes = "is_me_false"
