# Handles incoming messages and URLs unique for TeLLMgramBot
import re
from typing import Optional
import validators

from .initialize import init_url_prompt
from .utils import log_error
from .models import TokenLimits
from .web_utils import (
    fetch_url,
    strip_html_markup,
    InvalidURLException,
    InsecureURLException,
    SusURLException,
)
from .providers.factory import get_provider

def handle_greetings(text: str) -> Optional[str]:
    """
    Respond quickly with single-word greetings like these examples:
    - ' hello ' -> 'Hello!'
    - 'Hey...?' -> 'Hey!'
    - 'SUP?!?!' -> 'Sup!'
    """
    greetings = {'Hello', 'Hi', 'Hey', 'Heya', 'Sup', 'Yo'}
    word = re.sub(r'[^\w]', '', text.title().strip())
    if word in greetings:
        return f"{word}!"
    return None


def handle_common_queries(text: str) -> Optional[str]:
    """
    Send messages for assistant bot to respond quickly with some example phrases:
    - ' How you doing ' -> 'How YOU doin?'
    - 'What's up!' -> 'Wassup?'
    """
    phrase = re.sub(r'[^\w]', '', text.lower().strip())
    if phrase.startswith('howyoudoin'):
        return 'How YOU doin?'
    elif phrase == 'wassup' or phrase == 'whatup' or phrase == 'whatsup':
        return 'Wassup?'
    return None

async def handle_url_ask(text: str, model: str = 'gpt-4o') -> Optional[str]:
    """
    Process URL content in an LLM to provide a summary.

    Extracts URLs wrapped in square brackets [], validates them, checks for
    safety via VirusTotal, fetches content, and summarizes via an LLM specified
    by model name. Errors are logged via the logging system categorized by model
    and error type.

    Args:
        text: The message text potentially containing a URL in [square brackets].
        model: The LLM model to use for URL summarization (default: 'gpt-4o').

    Returns:
        A summary string if a URL was found and processed successfully, an error
        message string if processing failed, or None if no URL detected in text.

    Raises:
        No exceptions are raised; all errors are caught and logged, returning
        user-friendly error messages instead.
    """
    url_match = re.search(r'\[http(s)?://\S+]', text.strip())
    if url_match:
        # Extract the URL from the message, but not the square brackets
        url = url_match.group()[1:-1]

        # Fetch the URL content
        try:
            # The function strips the HTML markup and ensures the URL is valid and safe
            url_content = strip_html_markup(await fetch_url(url))

            # Check if the URL is valid real quick
            if not validators.url(url):
                raise InvalidURLException(f"Invalid URL parsed by message_handlers.handle_url_ask(): {url}")

            # Build messages:
            # 1. URL content to be added into the system prompt template
            # 2. User message requesting URL in [square brackets]
            messages = [
                {"role": "system", "content": url_content},
                {"role": "user", "content": text}
            ]

            # Consider the maximum amount of tokens a LLM can support.
            # If the URL content is too big, we need to prune it down to a reasonable size.
            # Let's also reserve 500 tokens for prompt and response.
            lengthy_url = False
            pruned_tail = ''
            token_model = TokenLimits(model)
            token_count = await token_model.num_tokens_from_messages(messages)
            token_limit = token_model.max_tokens() - 500
            if token_count > token_limit:
                lengthy_url = True
                while token_count > token_limit:
                    # Remove every last word until the token limit is satisfied
                    messages[0]["content"] = messages[0]["content"].rsplit(' ', 1)[0]
                    token_count = await token_model.num_tokens_from_messages(messages)
                # Show the last 50 characters of the pruned URL content
                pruned_tail = messages[0]["content"][-50:]

            # Load system prompt template to insert URL content
            template = init_url_prompt('url_analysis.prmpt')
            messages[0]["content"] = template.format(url_content=messages[0]["content"])

            # Call the LLM for the response that summarizes URL content
            try:
                response = await get_provider(model).complete(model, messages)
            except Exception as e:
                log_error(e, f"{model} URL")
                return "Something went wrong while fetching the URL. Please try again later."

            # If the URL content was too long, let the user know
            if lengthy_url:
                response += ("\n\n"
                    "*NOTE*: The URL content was too long and needed to be pruned for my summary."
                    f" If the text after \"{pruned_tail}\" is crucial, insert the rest for me."
                )
            return response

        except InvalidURLException as e:
            log_error(e, 'URL')
            return "The URL you provided appears to be invalid. Could you please check it and try again?"
        except InsecureURLException:
            return ("The URL you provided is not secure. Could you please try another URL, or just pasting the "
                    "relevant content here?")
        except SusURLException:
            return ("The URL you provided is potentially unsafe, based on my internal scans. You can check the safety "
                    "of URLS using this site: https://www.virustotal.com/gui/home/url")
        except Exception as e:
            log_error(e, 'URL')
            return f"Something went wrong while fetching the URL: {e}"

    return None
