
import logging
import redis
from telethon import TelegramClient, events
from telethon.sessions import StringSession
import requests
import base64
import spacy
import mimetypes
import datetime
import json
import aiohttp
import re
import os

# Настройка логирования
logging.basicConfig(filename="/var/www/html/debug.txt", format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    level=logging.INFO, encoding="UTF-8")
logger = logging.getLogger(__name__)

nlp = spacy.load("ru_core_news_sm")

with open("/var/www/html/i.json", "r", encoding="UTF-8") as e:
    data_d = json.load(e)

api_id = base64.b64decode(data_d['api_id']).decode('utf-8')
api_hash = base64.b64decode(data_d['api_hash']).decode('utf-8')
phone_number = base64.b64decode(data_d['phone_number']).decode('utf-8')
file = base64.b64decode(data_d['file']).decode('utf-8')
group_link = data_d['group_link']

logger.debug("Старт.")

try:
    r = redis.Redis(host='localhost', port=6379, db=0)
except Exception as e:
    logger.info(f"Redis : "+str(e))

session = StringSession(base64.b64decode(data_d['file']).decode('utf-8'))
client = TelegramClient(session, api_id, api_hash)

async def extract_info(text):
    doc = nlp(text)

    # 1. Хэштеги
    hashtags = re.findall(r"#\w+", text)

    # 2. Цена (попытка)
    price_matches = re.findall(r"(?:за|цена|стоит|продам)\s*?[^\w\s]?\s*?(?:около\s*)?(\d[\d\s.,]*)(?:\s?руб|\s?₽|\s?р|\s?)", text, re.IGNORECASE)
    price = None
    if price_matches:
        price_str = price_matches[0]
        price_str = price_str.replace(" ", "").replace(",", "").replace(".", "")
        try:
            price = int(price_str)
        except ValueError:
            logger.info(f"Не удалось преобразовать '{price_str}' в число.")

    # 4. Размеры (очень сложно, spaCy не предназначен для этого)
    sizes = []
    for token in doc:
        if token.text.lower() == "размер" or token.text.lower() == "size":
            # Ищем следующие токены, надеясь, что это размеры
            size_tokens = []
            for next_token in doc[token.i+1:token.i+5]: #Чуть больше смотрим
                if next_token.text.isalpha() and len(next_token.text) <= 5 :
                    size_tokens.append(next_token)
                elif next_token.text == ",": #Разделитель
                   continue
                else:
                   break
            sizes = [s.text for s in size_tokens] #Извлекаем текст из токенов


    return [price, hashtags, sizes]


async def get_mime_type_from_file(file_path):
    mime_type, encoding = mimetypes.guess_type(file_path)
    return mime_type

async def download_photo_by_id(chat_id, target_photo_id):
    async for message in client.iter_messages(chat_id):
        if message.photo and message.photo.id == target_photo_id:
            path = await client.download_media(message.photo, file='/var/www/html/images/')
            return path
    return None

async def dont(id, images):
    r.delete(f"H56F{id}")

async def yg4(prompt):
    url = "https://llm.api.cloud.yandex.net/foundationModels/v1/completion"
    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer AQVNwKVDroxzpaWogNT6yF8dYzKw8Fv9sEz64A22"
    }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, headers=headers, json=prompt) as response:
            return await response.text()

async def check_publish(text = "", images = [], username = "", id_user="",sender=""):
    if (sender.username == None):
        return
    if (data_d['jobs'] != "1"):
        await dont(id_user,images)
        return
    if (username and data_d["stop"]):
        if (username in data_d['stop']):
            await dont(id_user,images)
            return None
    if (data_d['filter'] != ""):
        for teY in data_d['filter'].split(","):
            if (teY in text):
                await dont(id_user,images)
                return None
    if isinstance(images, list) == False :
        try:
            images = json.loads(images.decode('utf-8'))
        except (json.JSONDecodeError, AttributeError) as e:
            logger.error(f"Ошибка декодирования JSON: {e}")
            await dont(id_user, images)
            return None

    images_new = []
    for image in images:
        path = await download_photo_by_id(image[0], image[1])
        if path is None:
            logger.warning(f"Не удалось скачать изображение: {image}")
            continue  # Пропустить это изображение
        images_new.append(path)
        typem = await get_mime_type_from_file(path)
        if (typem != "image/jpeg" and typem != "image/png"):
            await dont(id_user,images)
            return None

    # Ограничение количества изображений
    images_new = images_new[:5] # Отправляем только первые 5 изображений

    match = re.search(r"#пересыл", text, re.IGNORECASE)
    if match:
        pers = "1"
    else:
        pers = "0"
    try:
        responsem = await yg4({
            "modelUri": "gpt://b1gcnbkf3gd5die7fma6/yandexgpt-lite",
            "completionOptions": {
                "stream": False,
                "temperature": 0.6,
                "maxTokens": "2000"
            },
            "messages": [
                {
                    "role": "user",
                    "text": "Привет. Есть текст "+text+". нужно из него извлечь данные, в таком формате {'0' : {'name' : '', 'size' : '', 'price' : '', 'username' : '', 'city' : ''}, '1' : {'name' : '', 'size' : '', 'price' : '', 'username' : '', 'city' : ''}}.  Если есть данные, то ты их вставляешь.Город не может быть username, username должен заполнятся, если в начале '@' ,а потом английские буквы. Город может быть в хэштэгах или в тексте, если он написан в сокращение, то вставляй полную форму. Цена только отформатированная, копейки округляй, и оставляй целую часть, без валюты и других знаков. если несколько данных, то в json второй добавляешь с теми ключами и так далее. Если размер указан только один, а данных несколько, то указываешь его для всех. Если размеров несколько, то есть через тире, то указываешь только один. Размер конвертировать например в XXS, XS,S,M,L,XL,XXL,XXXL,XXXL. БЕЗ ЛИШНИХ КОММЕНТАРИЕВ"
                },
            ]
        })
        response = json.loads(responsem)['result']['alternatives'][0]['message']['text'].replace("```", "")
    except Exception as e:
        logger.error(f"Ошибка при вызове g4f.ChatCompletion: {e}")
        await dont(id_user, images)
        return None

    if not isinstance(response, str):
        data = await extract_info(text)
        if (data[0] == None or data[0] == "0"):
            await dont(id_user,images)
            return None

        req = requests.post("https://geniro.ru/tme/setData",params={
            "name" : f"Обьявление от {username}",
            "desc" : text,
            "tg" : sender.username,
            "price" : data[0],
            "image" : "|".join(images_new),
            "size" : "",
            "city" : "Москва",
            "id" : sender.id,
            "pers" : pers
        })

        logger.info(f"Ответ от сервера публикации : {req.text}")
        return None

    try:
        if not response:  # Проверка на пустую строку
            logger.warning("g4f вернул пустой ответ.")
            await dont(id_user, images)
            return None
        data = json.loads(response)
    except json.JSONDecodeError as e:
        logger.error(f"Ошибка декодирования JSON от g4f: {e}, Response: {response}")
        await dont(id_user, images)
        return None

    row = data['0']
    if (len(row['name']) < 15):
        name = row['name'] + " . Опубликовал " + datetime.date.today().strftime("%Y-%m-%d")
    else:
        name = row['name']
    logger.info(f"Название : {name}")
    req = requests.post("https://geniro.ru/tme/setData",params={
        "name" : name,
        "desc" : text,
        "tg" : sender.username,
        "price" : row['price'],
        "image" : "|".join(images_new),
        "size" : row['size'],
        "city" : row['city'],
        "id" : sender.id,
        "pers" : pers
    })
    logger.info(f"Ответ от сервера публикации : {req.text}")


async def main():
    logger.info("Скрипт запущен")
    try:
        await client.start(phone=phone_number)

        try:
            group = await client.get_entity(group_link)
            group_id = group.id

            @client.on(events.NewMessage(chats=group_id))
            async def handle_new_message(event):
                if event.message.grouped_id is None:
                    message = event.message.message
                    sender = await event.get_sender()

                    if sender is None:
                        logger.warning("Не удалось получить информацию об отправителе сообщения.")
                        return

                    all_photo = False
                    if event.message.media and hasattr(event.message.media, 'photo'):
                        all_photo = [[group_id, event.message.media.photo.id]]

                    redis_key = f"H56F{sender.id}"
                    redis_photos = r.get(redis_key)

                    if redis_photos:
                        try:
                            redis_photos = json.loads(redis_photos.decode('utf-8'))  # Декодируем из bytes
                            if isinstance(redis_photos, list):
                                if all_photo:
                                    all_photo.extend(redis_photos)
                                else:
                                    all_photo = redis_photos
                            else:
                                logger.warning(f"Некорректные данные в Redis ({redis_key}): ожидался список.  Сбрасываем.")
                                all_photo = False  # Сбрасываем фотографии
                                r.delete(redis_key)  # Удаляем некорректные данные
                        except json.JSONDecodeError as e:
                            logger.error(f"Ошибка декодирования JSON из Redis ({redis_key}): {e}. Сбрасываем.")
                            all_photo = False # Сбрасываем фотографии
                            r.delete(redis_key) # Удаляем некорректные данные
                        except Exception as e:
                            logger.error(f"Непредвиденная ошибка при работе с Redis ({redis_key}): {e}.  Сбрасываем фотографии.")
                            all_photo = False
                            r.delete(redis_key)

                    if message and message.strip() and all_photo:  # Проверяем, что message не пустая строка или пробелы
                        username = re.search(r"@\w+", message)
                        if username:
                            username = username.group(0).replace("@", "")
                        else:
                            username = sender.username

                        if username: # Проверяем, что username не пустой
                            await check_publish(message, all_photo, username, sender.id, sender)
                        else:
                            logger.warning("Не удалось извлечь username.")

                    elif all_photo:
                        try:
                            r.set(redis_key, json.dumps(all_photo), ex=900)
                        except TypeError as e:
                            logger.error(f"Ошибка при сериализации JSON для Redis ({redis_key}): {e}. all_photo = {all_photo}")
                        except Exception as e:
                            logger.error(f"Непредвиденная ошибка при сохранении в Redis ({redis_key}): {e}")

                @client.on(events.Album(chats=group_id))
                async def album_handler(event):
                    album_id = event.grouped_id
                    if album_id is None:
                        logger.warning("Получен Album event без grouped_id. Это странно.")
                        return

                    if r.get(f"album_processed:{album_id}"):
                        logger.info(f"Альбом {album_id} уже обработан. Пропускаем.")
                        return
                    
                    r.set(f"album_processed:{album_id}", "1", ex=3600)
                    sender = await event.get_sender()

                    if sender is None:
                        logger.warning("Не удалось получить информацию об отправителе сообщения (album).")
                        return

                    sender_username = sender.username
                    all_photo = []
                    text = ""
                    for message in event.messages:
                        if message.message:
                            text = message.message
                        if message.media and hasattr(message.media, 'photo'):
                            all_photo.append([group_id, message.media.photo.id])

                        redis_photos = r.get(f"H56F{sender.id}")
                        if redis_photos:
                            try:
                                all_ph = all_photo
                                all_photo = json.loads(redis_photos)
                                all_photo.extend(all_ph)
                            except json.JSONDecodeError as e:
                                logger.error(f"Ошибка декодирования JSON из Redis (album): {e}")
                                all_photo = all_ph
                    if text != "":
                        username = sender.username
                        if username != False and username != "":
                            await check_publish(text,all_photo,username,sender.id, sender)
                        else:
                            if all_photo:
                                r.set(f"H56F{sender.id}", json.dumps(all_photo), ex=900)
            logger.info(f"Клиент запущен. Слушаем группу {group_id}...")

        except ValueError as e:
            logger.error(f"Ошибка при получении ID группы. Проверьте ссылку и права доступа. Ошибка: {e}")
        await client.run_until_disconnected()

    except Exception as e:
        logger.exception(f"Общая ошибка: {e}")

    finally:
        logger.info("Завершение работы клиента.")
        await client.disconnect()

if __name__ == '__main__':
  import asyncio
  asyncio.run(main())