import re
from difflib import SequenceMatcher


def normalize(text):
    """Lowercase, strip, collapse whitespace."""
    return re.sub(r'\s+', ' ', text.lower().strip())


def word_overlap_score(query_words, text):
    """Score how many query words appear in text."""
    text_lower = text.lower()
    return sum(1 for w in query_words if w in text_lower)


def similarity(a, b):
    """Simple string similarity ratio."""
    return SequenceMatcher(None, a.lower(), b.lower()).ratio()


def generate_response(chat, user_message):
    """
    Match user message against vendor data (FAQs, products, policies, description).
    Returns (response_text, suggests_handoff) tuple.
    """
    try:
        vendor_config = chat.vendor.ai_config
    except Exception:
        return ("This shop's assistant isn't set up yet. Please contact the vendor directly.", True)

    vendor = vendor_config.vendor
    message = normalize(user_message)
    words = [w for w in message.split() if len(w) > 2]

    # --- 1. Try FAQ matching ---
    best_faq = None
    best_faq_score = 0

    for faq in vendor_config.faqs.filter(is_active=True):
        sim = similarity(message, normalize(faq.question))
        overlap = word_overlap_score(words, faq.question) / max(len(words), 1)
        score = max(sim, overlap)
        if score > best_faq_score:
            best_faq_score = score
            best_faq = faq

    if best_faq and best_faq_score >= 0.45:
        return (best_faq.answer, False)

    # --- 2. Try product matching ---
    from products.models import Product
    products = Product.objects.filter(vendor=vendor, status='active')

    price_keywords = ['price', 'cost', 'how much', 'bei', 'shilingi', 'ksh', 'kes']
    asking_price = any(kw in message for kw in price_keywords)

    avail_keywords = ['available', 'in stock', 'do you have', 'stock', 'iko']
    asking_availability = any(kw in message for kw in avail_keywords)

    matched_products = []
    for product in products:
        name_score = word_overlap_score(words, product.name)
        desc_score = word_overlap_score(words, product.description or '') * 0.5
        cat_score = word_overlap_score(words, product.category or '') * 0.3
        score = name_score + desc_score + cat_score
        if score >= 1.5:
            matched_products.append((product, score))

    matched_products.sort(key=lambda x: x[1], reverse=True)

    if matched_products:
        if asking_price and len(matched_products) == 1:
            p = matched_products[0][0]
            response = f"{p.name} is KES {p.price}"
            if p.old_price and float(p.old_price) > float(p.price):
                response += f" (was KES {p.old_price})"
            if p.is_negotiable:
                response += ". The price is negotiable."
            return (response, False)

        if asking_availability and len(matched_products) == 1:
            p = matched_products[0][0]
            if p.is_available:
                return (f"Yes, {p.name} is available! It's KES {p.price}.", False)
            else:
                return (f"Sorry, {p.name} is currently out of stock.", False)

        top = matched_products[:5]
        lines = []
        for p, _ in top:
            line = f"- {p.name}: KES {p.price}"
            if not p.is_available:
                line += " (Out of stock)"
            if p.is_negotiable:
                line += " (Negotiable)"
            lines.append(line)
        intro = "Here's what I found:" if len(top) > 1 else "I found this:"
        return (f"{intro}\n" + "\n".join(lines), False)

    # --- 3. Check for general info keywords ---
    greeting_keywords = ['hi', 'hello', 'hey', 'habari', 'sasa', 'niaje', 'mambo']
    if any(message.strip() == kw or message.startswith(kw + ' ') for kw in greeting_keywords):
        return (f"Hi! Welcome to {vendor.shop_name}. How can I help you today? You can ask about our products, prices, or policies.", False)

    policy_keywords = ['return', 'refund', 'warranty', 'delivery', 'shipping', 'payment', 'policy', 'policies', 'mpesa', 'pay']
    if any(kw in message for kw in policy_keywords):
        if vendor_config.policies:
            return (vendor_config.policies, False)
        return (f"For details about our policies, please contact {vendor.shop_name} directly.", True)

    about_keywords = ['about', 'who are you', 'tell me about', 'what do you sell', 'what does', 'describe']
    if any(kw in message for kw in about_keywords):
        if vendor_config.business_description:
            return (vendor_config.business_description, False)
        return (f"{vendor.shop_name} is a {vendor.shop_category} shop located in {vendor.town_area}.", False)

    location_keywords = ['where', 'location', 'located', 'address', 'find you', 'directions', 'wapi', 'mahali']
    if any(kw in message for kw in location_keywords):
        county = vendor.user.county or ''
        return (f"{vendor.shop_name} is located in {vendor.town_area}{', ' + county if county else ''}.", False)

    product_list_keywords = ['products', 'what do you sell', 'catalog', 'catalogue', 'items', 'bidhaa', 'all products']
    if any(kw in message for kw in product_list_keywords):
        top_products = products[:8]
        if top_products:
            lines = [f"- {p.name}: KES {p.price}" for p in top_products]
            more = products.count() - len(top_products)
            result = f"Here are some of our products:\n" + "\n".join(lines)
            if more > 0:
                result += f"\n\n...and {more} more. Browse the shop to see everything!"
            return (result, False)
        return ("We don't have any products listed at the moment. Check back soon!", False)

    hours_keywords = ['hours', 'open', 'close', 'time', 'working hours', 'saa']
    if any(kw in message for kw in hours_keywords):
        if vendor_config.custom_instructions and any(kw in vendor_config.custom_instructions.lower() for kw in ['hour', 'open', 'time']):
            return (vendor_config.custom_instructions, False)
        return (f"For our working hours, please contact {vendor.shop_name} directly.", True)

    # --- 4. Partial FAQ match (lower threshold) ---
    if best_faq and best_faq_score >= 0.3:
        return (best_faq.answer, False)

    # --- 5. Fallback: don't have enough knowledge ---
    return (
        f"I don't have enough information to answer that question accurately. "
        f"I can only help with questions about our products, prices, policies, and location. "
        f"For anything else, please reach out to {vendor.shop_name} directly — they'll be happy to assist you!",
        True
    )
