import re
import time
import shelve
import requests

from aiogram.methods import AnswerGuestQuery, SendPoll
from aiogram.types import InlineQueryResultArticle, InputTextMessageContent, InputRichMessageContent, InputRichMessage, Message
from aiogram.enums import ChatType

from vars import *
from config import *


def is_blocked(user_id):
    current_time = time.time()
    if user_id and user_id in last_command_time:
        time_diff = current_time - last_command_time[user_id][0]
        if user_id in last_error_time:
            time_error_diff = current_time - last_error_time[user_id]
        else:
            last_error_time[user_id] = current_time
            time_error_diff = 1984
        
        if time_diff < 3:
            alo = "🚫 *файл не вошёл.* хватит так быстро слать свои сообщения.\n" \
                f"подожди {round(3 - time_diff, 3)} секунд, брат😡😡"
        elif last_command_time[user_id][1] and time_diff < 5:
            alo = "🚫 *файл не вошёл.* я ещё не сгенерировал прошлое сообщение.\n" \
                f"подожди {round(10 - time_diff, 3)} секунд, чтобы я закрыл глаза " \
                "на это da."
        elif len(last_command_time[user_id][2]) > 1 and time_diff > 5:
            last_command_time[user_id] = [current_time, False, []]
            return "", False
        elif len(last_command_time[user_id][2]) > 1:
            alo = "🚫 *файл не вошёл.* хватит так быстро слать свои сообщения."
        else:
            return "", False

        
        
        if time_error_diff < 2:
            return "", True
        else:
            last_error_time[user_id] = current_time
            return alo, True
    return "", False


def is_replied_bot(message, is_channel, user_text, config=None):
    if message.chat.type in [ChatType.GROUP, ChatType.SUPERGROUP, ChatType.CHANNEL]:
        if not is_channel:
            is_reply_to_bot = (
                message.reply_to_message
                and message.reply_to_message.from_user
                and message.reply_to_message.from_user.is_bot
                and message.reply_to_message.from_user.username == BOT_USERNAME
            )
            if config:
                if config.get("call") == "da":
                    mentions_bot = any(
                        user_text.lower().split(" ", 1)[0] == call for call in BOT_CALLS
                    )
                    for call in BOT_CALLS:
                        if call.startswith("@"):
                            if call.lower() in user_text.lower():
                                mentions_bot = True
                else:
                    mentions_bot = False
            else:
                mentions_bot = any(
                    user_text.lower().startswith(call.lower()) for call in BOT_CALLS
                )
                for call in BOT_CALLS:
                    if call.startswith("@"):
                        if call.lower() in user_text.lower():
                            mentions_bot = True
            if not (is_reply_to_bot or mentions_bot):
                return False
    return True

async def g_answer(
    message, 
    text, 
    title="da", 
    id="1", 
    parse_mode="Markdown",
    reply_markup=None,
    user_id=None
):
    input_message_content = InputTextMessageContent(
        message_text=text,
        parse_mode=parse_mode
    )
    if user_id:
        config = get_user_config(user_id)
        if config["markdown"] == "new":
            input_message_content=InputRichMessageContent(
                rich_message=InputRichMessage(markdown=text)
            )
    return await message.answer_guest_query(
        result=InlineQueryResultArticle(
            id=id,
            reply_markup=reply_markup,
            title=title,
            input_message_content=input_message_content
        )
    )


async def answer_poll(
    data,
    message,
    question,
    options,
    is_anonymous=True,
    poll_type="regular",
    allows_multiple_answers=False,
    correct_option_id=None,
    user_id=None
):
    return await data["bot"](
        SendPoll(
            chat_id=user_id,
            question=question,
            options=options,
            is_anonymous=is_anonymous,
            type=poll_type,
            allows_multiple_answers=allows_multiple_answers,
            correct_option_id=correct_option_id,
        )
    )


def send_to_vosk(audio):
    files = {
        "audio": (
            "voice.ogg",
            audio,
            "audio/ogg"
        )
    }

    r = requests.post(
        VOSK_API,
        files=files,
        timeout=15
    )

    return r.json()


async def mans(message, text):
    if message.guest_query_id:
        return await message.answer_guest_query(
            result=InlineQueryResultArticle(
                id=1,
                reply_markup=None,
                title="da",
                input_message_content=InputTextMessageContent(
                    message_text=text,
                    parse_mode="Markdown"
                )
            )
        )
    else:
        return await message.reply(text)
    
def graph_emoji(status):
    match status:
        case "done":
            return "✅"
        case "error":
            return "🚫"
        case "make":
            return "💭"
        case "wait":
            return "⏳"
        case _:
            return "🥺"


def make_graph(message, process):
    text = ""
    replied = message.reply_to_message or None
    user_id = message.from_user.id
    config = get_user_config(user_id)
    im_model = IMAGE_MODEL.split("*", 1)
    model = config["model"].split("*", 1)
    photo_a = process.get("photo_a", None)
    photo = process.get("photo", None)
    stt = process.get("stt", None)
    t = process.get("text", None)
    
    if photo_a:
        text += f"{graph_emoji(photo_a)} анализ фото (в ответе)\n"
    if photo:
        text += f"{graph_emoji(photo)} анализ фото\n"
    if stt:
        text += f"{graph_emoji(stt)} stt (голос -> текст)\n"
        
    text += f"{graph_emoji(t)} запрос `{model[1]}` (`{model[0]}`)\n"
    
    if replied and replied.document:
        text += f"↳ {replied.document.file_size} файл (в ответе)\n"
    if message.document:
        text += f"↳ {message.document.file_size} файл\n"
        
    if replied and replied.photo:
        text += "↳ фото (в ответе)\n"
    if message.photo:
        text += "↳ фото\n"
        
    if replied and replied.location:
        text += "↳ геолокация (в ответе)\n"
    if message.location:
        text += "↳ геолокация\n"
        
    if replied and replied.poll:
        text += "↳ опрос (в ответе)\n"
    if message.poll:
        text += "↳ опрос\n"  
      
    if replied and replied.voice:
        text += "↳ голосовое сообщение (в ответе)\n"
    if message.voice:
        text += "↳ голосовое сообщение\n"
      
    if replied and replied.text:
        text += f"↳ {len(replied.text)} текст в ответе\n"

    return text


def clean_tail_repeats(text: str, max_repeat: int = 10):
    if not text:
        return text, 0

    original_length = len(text)
    cleaned = text

    pattern = rf"(.)\1{{{max_repeat},}}$"

    def limit_repeats(match):
        char = match.group(1)
        return char * max_repeat

    cleaned = re.sub(pattern, limit_repeats, cleaned)

    pattern_space = rf"((\S)\s+)\2{{{max_repeat},}}$"
    cleaned = re.sub(
        pattern_space,
        lambda m: (m.group(2) + " ") * min(max_repeat, len(m.group(1))),
        cleaned,
    )

    words = cleaned.split()
    if len(words) >= 2:
        for i in range(1, min(len(words), 10)):
            if all(words[-j] == words[-j - 1] for j in range(i)):
                unique_words = words[: -(i + 1)]
                cleaned = " ".join(unique_words + [words[-1]])
                break

    removed_count = original_length - len(cleaned)

    cleaned = re.sub(r" +", " ", cleaned)
    cleaned = cleaned.strip()

    return cleaned, removed_count


def parse_response(text: str):
    if "connection error." in text:
        return {
            "type": "connection",
            "message": "ошибка подключения к провайдеру. подожди или смени провайдера в настройках узбекгпт.",
        }
    elif "model quota exceeded" in text:
        return {
            "type": "quota",
            "message": "лимит токенов закончился. подожди или смени модель.",
        }
    elif "tier capacity exceeded." in text:
        return {"type": "tier", "message": "попробуй ещё раз!"}
    elif "internal server" in text:
        return {
            "type": "int",
            "message": "произошла ошибка на стороне провайдера. подожди или смени провайдера в настройках узбекгпт.",
        }
    elif "(incomplete chunked read)" in text:
        return {
            "type": "incomplete",
            "message": "сервер петух и оборвал соединение. подожди или смени провайдера в настройках узбекгпт.",
        }
    elif "provider error" in text:
        return {
            "type": "provider",
            "message": "врат ошибка на стороне провайдера модели. смени провайдера или модель.",
        }
    elif "rate limit" in text:
        return {
            "type": "quota",
            "message": "лимит токенов закончился у провайдера врат. подожди или смени модель.",
        }
    else:
        return {
            "type": "unknown",
            "message": f"произошла какая-то ошибка при генерации... {text}",
        }


def galockinator(text):
    for _ in range(random.randint(1, 3)):
        if random.random() < 0.3:
            text += "☝️"
        else:
            text += "✅"

    return text


def remove_think_tags(text):
    result = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
    result = re.sub(r"<thought>.*?</thought>", "", text, flags=re.DOTALL)
    return result


def get_all_users(only_notify=False):
    with shelve.open("users") as db:
        return {k: db[k] for k in db.keys() if not only_notify or db[k].get("notify") == "da"}


def get_all_user_ids(only_notify=False):
    ids = []
    with shelve.open("users") as db:
        for user_id in db.keys():
            if only_notify and db[user_id].get("notify") != "da":
                continue
            try:
                ids.append(int(user_id))
            except ValueError:
                pass
    return ids


def set_user_config(
    user_id, 
    model_name, 
    stream=DEFAULT_STREAM, 
    call=DEFAULT_CALL, 
    prompt=DEFAULT_PROMPT,
    notify=DEFAULT_NOTIFY,
    markdown=DEFAULT_MARKDOWN,
    prompts={},
):
    with shelve.open("users") as db:
        db[str(user_id)] = {
            "model": model_name,
            "stream": stream,
            "call": call,
            "prompt": prompt,
            "notify": notify,
            "markdown": markdown,
            "prompts": prompts,
        }



def set_user_setting(
    user_id, 
    model=None, 
    stream=None, 
    call=None, 
    prompt=None,
    notify=None,
    markdown=None,
    prompts=None,
):
    with shelve.open("users") as db:
        user_id = str(user_id)
        if user_id not in db:
            db[user_id] = {}
        
        settings = db[user_id]
        
        if model is not None: settings["model"] = model
        if stream is not None: settings["stream"] = stream
        if call is not None: settings["call"] = call
        if prompt is not None: settings["prompt"] = prompt
        if notify is not None: settings["notify"] = notify
        if markdown is not None: settings["markdown"] = markdown
        if prompts is not None: settings["prompts"] = prompts

        db[user_id] = settings


def delete_user_from_db(user_id):
    with shelve.open("users") as db:
        if str(user_id) in db:
            del db[str(user_id)]


def reset_user_setting(user_id, key, value):
    with shelve.open("users") as db:
        user = db[str(user_id)]
        user[key] = value
        db[str(user_id)] = user


def ensure_user(
    user_id,
    model_name=DEFAULT_MODEL,
    stream_mode=DEFAULT_STREAM,
    call_mode=DEFAULT_CALL,
    prompt_name=DEFAULT_PROMPT,
    notify_mode=DEFAULT_NOTIFY,
    markdown=DEFAULT_MARKDOWN,
    prompts={},
):
    with shelve.open("users") as db:
        key = str(user_id)

        if key not in db:
            db[key] = {
                "model": model_name,
                "stream": stream_mode,
                "call": call_mode,
                "prompt": prompt_name,
                "notify": notify_mode,
                "markdown": markdown,
                "prompts": prompts,
            }


def get_user_config(user_id):
    with shelve.open("users") as db:
        config = db.get(str(user_id), {})
        defaults = {
            "model": DEFAULT_MODEL,
            "stream": DEFAULT_STREAM,
            "call": DEFAULT_CALL,
            "prompt": DEFAULT_PROMPT,
            "notify": DEFAULT_NOTIFY,
            "markdown": DEFAULT_MARKDOWN,
            "prompts": {},
        }
        updated = False
        for key, value in defaults.items():
            if key not in config:
                config[key] = value
                updated = True
        if updated:
            db[str(user_id)] = config
    return config

