from flask import Blueprint, request, jsonify
from app.config import Config
from app import mysql
import uuid
import requests
import json
import re
import logging
from datetime import datetime, timedelta
from dateutil import parser
from dateutil.relativedelta import relativedelta
import pytz
import speech_recognition as sr
from pydub import AudioSegment
import io
from app.routes.api import parse_natural_date, schedule_meeting, create_meeting_prompt

# Set up logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

whatsapp_bp = Blueprint('whatsapp', __name__)

@whatsapp_bp.route('/webhook', methods=['GET', 'POST'])
def whatsapp_webhook():
    if request.method == 'GET':
        mode = request.args.get('hub.mode')
        token = request.args.get('hub.verify_token')
        challenge = request.args.get('hub.challenge')

        logger.info(f"Webhook verification request - Mode: {mode}, Token: {token}, Challenge: {challenge}")

        if not all([mode, token, challenge]):
            logger.error("Missing parameters in verification request")
            return 'Missing parameters', 400

        try:
            # Verify against company tokens
            cursor = mysql.connection.cursor()
            cursor.execute("""
                SELECT id FROM companies 
                WHERE whatsapp_verify_token = %s
            """, (token,))
            
            if cursor.fetchone() and mode == 'subscribe':
                logger.info("Webhook verification successful")
                return challenge, 200
            
            logger.error("Verification token mismatch")
            return 'Verification token mismatch', 403
            
        except Exception as e:
            logger.error(f"Database error during verification: {str(e)}")
            return 'Verification error', 500

    if request.method == 'POST':
        try:
            # logger.info(">> Received POST request")
            # logger.info(">> Headers: %s", dict(request.headers))
            # logger.info(">> Raw data: %s", request.get_data())
            
            data = request.json
            # logger.info(">> JSON payload: %s", json.dumps(data, indent=2))
            
            if not data:
                logger.error(">> No data received in POST request")
                return jsonify({"status": "error", "message": "No data received"}), 400

            # Check if this is a WhatsApp message
            if 'entry' in data and len(data['entry']) > 0:
                for entry in data['entry']:
                    # logger.info(">> Processing entry: %s", entry)
                    if 'changes' in entry:
                        for change in entry['changes']:
                            logger.info(">> Processing change: %s", change)
                            if (change.get('field') == 'messages' and 
                                'value' in change):
                                value = change['value']
                                # Store metadata in flask.g for access in process_message
                                from flask import g
                                g.whatsapp_metadata = value.get('metadata', {})
                                
                                if 'messages' in value:
                                    for message in value['messages']:
                                        # Add metadata to the message object
                                        message['metadata'] = g.whatsapp_metadata
                                        # logger.info(">> Processing message: %s", message)
                                        process_message(message)
            
            return jsonify({"status": "received"}), 200
            
        except Exception as e:
            logger.error(">> Error processing webhook: %s", str(e), exc_info=True)
            return jsonify({"status": "error", "message": str(e)}), 500

def get_or_create_session(company_id, customer_number):
    cursor = mysql.connection.cursor()
    # Try to find existing session
    cursor.execute("""
        SELECT session_id 
        FROM whatsapp_conversations 
        WHERE company_id = %s AND customer_number = %s
    """, (company_id, customer_number))
    
    result = cursor.fetchone()
    if result:
        return result[0]
    
    # If no session exists, create new one
    new_session_id = str(uuid.uuid4())
    cursor.execute("""
        INSERT INTO whatsapp_conversations (
            id, company_id, customer_number, session_id, 
            created_at, updated_at
        )
        VALUES (%s, %s, %s, %s, NOW(), NOW())
    """, (str(uuid.uuid4()), company_id, customer_number, new_session_id))
    mysql.connection.commit()
    return new_session_id

def store_message(company_id, session_id, role, content, customer_number, is_from_customer=True):
    cursor = mysql.connection.cursor()
    try:
        # Store the message
        cursor.execute("""
            INSERT INTO whatsapp_messages (
                id, company_id, session_id, role, content, 
                customer_number, is_from_customer, created_at
            )
            VALUES (%s, %s, %s, %s, %s, %s, %s, NOW())
        """, (
            str(uuid.uuid4()), company_id, session_id, role, 
            content, customer_number, is_from_customer
        ))
        
        # Update conversation last message time
        cursor.execute("""
            UPDATE whatsapp_conversations 
            SET updated_at = NOW() 
            WHERE company_id = %s AND customer_number = %s
        """, (company_id, customer_number))
        
        mysql.connection.commit()
    except Exception as e:
        logger.error("Error storing message: %s", str(e))
        mysql.connection.rollback()

def get_chat_history(company_id, customer_number):
    cursor = mysql.connection.cursor()
    cursor.execute("""
        SELECT role, content, created_at 
        FROM whatsapp_messages 
        WHERE company_id = %s AND customer_number = %s 
        ORDER BY created_at ASC
    """, (company_id, customer_number))
    return cursor.fetchall()

def process_message(message):
    try:
        # logger.info(">> Starting to process message: %s", message)
        from_number = message.get('from')
        
        # Get metadata from the parent object
        metadata = message.get('metadata', {})
        if not metadata:
            from flask import g
            metadata = getattr(g, 'whatsapp_metadata', {})
        
        company_phone = metadata.get('display_phone_number')
        phone_number_id = metadata.get('phone_number_id')
        
        # Get company details first to get access token
        cursor = mysql.connection.cursor()
        cursor.execute("""
            SELECT id, whatsapp_access_token 
            FROM companies 
            WHERE whatsapp_phone_number = %s
        """, (company_phone,))
        result = cursor.fetchone()
        
        if not result:
            logger.error(">> Company not found for WhatsApp number: %s", company_phone)
            return
            
        company_id, access_token = result
        
        # Handle different message types
        message_type = message.get('type')
        message_content = None
        
        if message_type == 'text':
            message_content = message.get('text', {}).get('body', '')
        elif message_type == 'audio':
            audio_data = message.get('audio', {})
            audio_id = audio_data.get('id')
            
            if audio_id:
                try:
                    # First, get the media URL
                    media_url = f"https://graph.facebook.com/v19.0/{audio_id}"
                    headers = {
                        "Authorization": f"Bearer {access_token}"
                    }
                    
                    # Get media URL
                    media_response = requests.get(media_url, headers=headers)
                    if media_response.status_code == 200:
                        media_data = media_response.json()
                        download_url = media_data.get('url')
                        
                        if download_url:
                            # Download the audio file
                            audio_response = requests.get(download_url, headers=headers)
                            if audio_response.status_code == 200:
                                # Convert audio to text
                                audio_data = io.BytesIO(audio_response.content)
                                audio = AudioSegment.from_file(audio_data, format="ogg")
                                
                                # Convert to WAV for speech recognition
                                wav_data = io.BytesIO()
                                audio.export(wav_data, format="wav")
                                wav_data.seek(0)
                                
                                # Perform speech recognition
                                recognizer = sr.Recognizer()
                                with sr.AudioFile(wav_data) as source:
                                    audio_content = recognizer.record(source)
                                    message_content = recognizer.recognize_google(audio_content)
                                    logger.info(">> Voice transcription: %s", message_content)
                except Exception as e:
                    logger.error(">> Error processing voice message: %s", str(e))
                    message_content = None
        
        if not from_number or not message_content:
            logger.error(">> Missing required message data")
            if from_number and message_type == 'audio':
                # Send error message for voice processing
                return send_whatsapp_message(
                    from_number,
                    "I couldn't understand the voice message. Could you please type your message or try recording again?",
                    phone_number_id,
                    access_token
                )
            return

        # Clean the phone number
        customer_number = from_number.replace('whatsapp:', '')
        
        # logger.info(">> Found company ID: %s", company_id)
        
        # Get or create session
        session_id = get_or_create_session(company_id, customer_number)
        
        # Store customer message with the actual transcribed content
        store_message(company_id, session_id, 'user', message_content, customer_number, True)
        
        # Get chat history for AI context
        chat_history = get_chat_history(company_id, customer_number)
        
        # Check if this is a confirmation message
        if message_content.lower() == 'confirm':
            try:
                # Get the last meeting details message
                cursor.execute("""
                    SELECT content 
                    FROM whatsapp_messages 
                    WHERE company_id = %s 
                    AND customer_number = %s 
                    AND content LIKE '%%Please review these meeting details%%'
                    ORDER BY created_at DESC 
                    LIMIT 1
                """, (company_id, customer_number))
                
                meeting_details = cursor.fetchone()
                if meeting_details:
                    # Extract email, date, and time from the message
                    content = meeting_details[0]
                    email_match = re.search(r'Email:\s*([\w\.-]+@[\w\.-]+)', content)
                    date_match = re.search(r'Date:\s*(.*?)(?:\n|$)', content)
                    time_match = re.search(r'Time:\s*(\d{1,2}:\d{2})', content)
                    
                    if email_match and date_match and time_match:
                        action_data = {
                            "action": "schedule_meeting",
                            "email": email_match.group(1).strip(),
                            "date": date_match.group(1).strip(),
                            "time": time_match.group(1).strip()
                        }
                        
                        # Import schedule_meeting function
                        from app.routes.api import schedule_meeting
                        
                        # Schedule the meeting
                        meeting_response = schedule_meeting(company_id, action_data)
                        
                        if isinstance(meeting_response, tuple):
                            meeting_data = meeting_response[0].get_json()
                        else:
                            meeting_data = meeting_response.get_json()
                        
                        if meeting_data.get('success'):
                            response_text = meeting_data.get('message', 'Meeting scheduled successfully!')
                        else:
                            response_text = meeting_data.get('error', 'Failed to schedule meeting. Please try again.')
                    else:
                        response_text = "Sorry, I couldn't find all the required meeting details. Please try scheduling again."
                else:
                    response_text = "Sorry, I couldn't find the meeting details to confirm. Please try scheduling again."
                    
            except Exception as e:
                logger.error(f"Error processing meeting confirmation: {str(e)}", exc_info=True)
                response_text = "Sorry, there was an error scheduling the meeting. Please try again."
                
        # If not a confirmation, process normally with AI
        else:
            # Process with AI including chat history
            response_text = process_with_ai(message_content, company_id, chat_history)
            
            # Check if the response contains [ACTION] tags
            if '[ACTION]' in response_text:
                action_pattern = re.compile(r'\[ACTION\](.*?)\[/ACTION\]', re.DOTALL)
                action_match = action_pattern.search(response_text)
                if action_match:
                    try:
                        action_data = json.loads(action_match.group(1))
                        # Remove the action tags but keep the response text
                        # We'll store the meeting details but not schedule until confirmed
                        response_text = re.sub(r'\[ACTION\].*?\[/ACTION\]', '', response_text).strip()
                    except Exception as e:
                        logger.error(f"Error parsing action data: {str(e)}")
        
        # Store AI response
        store_message(company_id, session_id, 'assistant', response_text, customer_number, False)
        
        # Send response back to WhatsApp
        result = send_whatsapp_message(from_number, response_text, phone_number_id, access_token)
        logger.info(">> WhatsApp API response: %s", result)
        
    except Exception as e:
        logger.error(">> Error in process_message: %s", str(e), exc_info=True)

def process_with_ai(message, company_id, chat_history):
    try:
        cursor = mysql.connection.cursor()
        
        # Get company information including both instructions and documents
        cursor.execute("""
            SELECT 
                c.name, 
                c.instructions, 
                c.website,
                GROUP_CONCAT(DISTINCT d.file_content SEPARATOR '\n---\n') as doc_content,
                c.welcome_message
            FROM companies c 
            LEFT JOIN company_documents d ON c.id = d.company_id 
            WHERE c.id = %s
            GROUP BY c.id
        """, (company_id,))
        
        company = cursor.fetchone()
        if not company:
            return "Company not found"

        # Get company settings
        cursor.execute("""
            SELECT available_days, start_time, end_time 
            FROM company_settings 
            WHERE company_id = %s
        """, (company_id,))
        settings = cursor.fetchone()
        
        # Format settings for the system message
        settings_info = ""
        if settings:
            available_days = settings[0].split(',') if settings[0] else []
            start_time = settings[1]
            end_time = settings[2]
            settings_info = f"""
            Meeting Availability:
            - Available Days: {', '.join(available_days)}
            - Time Slot: {start_time} to {end_time}
            """
        else:
            settings_info = "No meeting settings configured yet."

        # Get website URLs - matching web widget format
        cursor.execute("""
            SELECT url, page_title 
            FROM website_pages 
            WHERE company_id = %s
        """, (company_id,))
        available_urls = cursor.fetchall()
        
        # Format URLs exactly like web widget
        url_context = "Available pages:\n"
        if available_urls:
            for url, title in available_urls:
                url_context += f"- {title}: {url}\n"
        else:
            url_context = f"Main website: {company[2]}\n"

        # Format company info exactly like web widget
        company_info = ""
        if company[1]:  # instructions
            company_info += f"General Information:\n{company[1]}\n\n"
        
        if company[3]:  # doc_content
            company_info += f"Additional Information from Documents:\n{company[3]}\n\n"

        # Create system message with all information - exactly matching web widget format
        system_message = {
            "role": "system",
            "content": f"""You are a customer service representative for {company[0]}. 
            Keep your responses brief and professional.

            Company Information:
            {company_info}

            Website Information:
            {url_context}

            {settings_info}

            Meeting Scheduling Instructions:
            {create_meeting_prompt(company[0])}

            Guidelines:
            1. Use information from both the provided instructions and PDF documents to give accurate responses
            2. Keep responses concise and relevant
            3. If information is available in multiple sources, combine it appropriately
            4. For website-related queries, only use the URLs provided above
            5. If you don't have enough information to answer accurately, say so
            6. For meeting requests:
               - IMMEDIATELY CHECK if the requested day is in available days ({', '.join(available_days) if settings and settings[0] else 'none configured'})
               - If day is not available, DO NOT proceed with time selection
               - Instead, inform the user that the requested day is not available and list the available days
               - Only proceed with scheduling if the requested day is available
            7. If customer starts conversation with hello or similar greetings, respond briefly and ask how you can help
            8. Track meeting scheduling state and only proceed when all required information is collected
            9. When scheduling meetings, ensure the requested day and time are within available slots: {settings_info if settings else 'No settings configured'}
            10. NEVER ask for time if the requested day is not available

            When detecting a meeting request:
            - First validate the requested day against available days
            - If day is not available, respond with available days only, DO NOT ask for time
            - Only if day is available, proceed with [ACTION] tag
            Format: [ACTION]{{"action": "schedule_meeting", "email": "...", "date": "...", "time": "..."}}[/ACTION]
            For the date field:
            - If the user provides a natural language date (e.g., "next Monday,Tomorrow"), use that exact phrase
            - If the user provides a specific date (e.g., "2024-03-20"), use that format
            - DO NOT include any additional text or formatting instructions in the date field
            Ensure the JSON inside [ACTION] tags is valid — use double quotes, include all commas, and no comments or extra text.
            """
        }

        # Construct messages array with history - matching web widget format
        messages = [system_message]
        for role, content, _ in chat_history:
            messages.append({"role": role, "content": content})
        messages.append({"role": "user", "content": message})

        # Call Groq API - matching web widget configuration
        response = requests.post(
            Config.GROQ_API_URL,
            headers={
                "Authorization": f"Bearer {Config.GROQ_API_KEY}",
                "Content-Type": "application/json"
            },
            json={
                "model": "meta-llama/llama-4-scout-17b-16e-instruct",
                "messages": messages,
                "temperature": 1,  # Matching web widget temperature
                "max_tokens": 1000,
                "top_p": 1,
                "stream": False
            }
        )
        
        if response.status_code == 200:
            ai_response = response.json()["choices"][0]["message"]["content"]

            # Handle meeting scheduling exactly like web widget
            action_pattern = re.compile(r'\[ACTION\](.*?)\[/ACTION\]', re.DOTALL)
            action_match = action_pattern.search(ai_response)

            if action_match:
                try:
                    action_data = json.loads(action_match.group(1))
                    if action_data.get('action') == 'schedule_meeting':
                        # Clean up date field exactly like web widget
                        if 'date' in action_data:
                            date_str = action_data['date']
                            date_str = re.sub(r'\s*\(.*?\)\s*', '', date_str)
                            date_str = re.sub(r'\s*date\s*(is)?\s*', '', date_str, flags=re.IGNORECASE)
                            action_data['date'] = date_str.strip()
                        
                        meeting_response = schedule_meeting(company_id, action_data)
                        if isinstance(meeting_response, tuple):
                            meeting_data = meeting_response[0].get_json()
                        else:
                            meeting_data = meeting_response.get_json()
                        if meeting_data.get('success'):
                            return meeting_data.get('message', 'Meeting scheduled successfully!')
                        else:
                            return meeting_data.get('error', 'Failed to schedule meeting. Please try again.')
                except Exception as e:
                    logger.error("Error scheduling meeting: %s", str(e))
                    return f"Error scheduling meeting: {str(e)}"

            # Remove any remaining [ACTION] tags from the response
            ai_response = re.sub(r'\[ACTION\].*?\[/ACTION\]', '', ai_response).strip()
            return ai_response
        else:
            logger.error(f"Groq API error: {response.text}")
            return "I apologize, but I'm having trouble processing your request right now. Please try again in a moment."
            
    except Exception as e:
        logger.error("Error in process_with_ai: %s", str(e), exc_info=True)
        return "I apologize, but there was an error processing your message. Please try again."

def send_whatsapp_message(to_number, message, phone_number_id, access_token):
    # Remove 'whatsapp:' prefix if present
    to_number = to_number.replace('whatsapp:', '')
    
    url = f"https://graph.facebook.com/v19.0/{phone_number_id}/messages"
    
    headers = {
        "Authorization": f"Bearer {access_token}",
        "Content-Type": "application/json"
    }
    data = {
        "messaging_product": "whatsapp",
        "to": to_number,
        "type": "text",
        "text": {"body": message}
    }
    
    response = requests.post(url, headers=headers, json=data)
    response_json = response.json()
    
    # Check for token expiration
    if response.status_code == 401 or (
        isinstance(response_json, dict) and 
        response_json.get('error', {}).get('code') == 190
    ):
        logger.info("Token expired, attempting to refresh...")
        
        # Get company_id from phone_number_id
        cursor = mysql.connection.cursor()
        cursor.execute("""
            SELECT id, whatsapp_app_id, whatsapp_app_secret 
            FROM companies 
            WHERE whatsapp_phone_number_id = %s
        """, (phone_number_id,))
        
        result = cursor.fetchone()
        if result:
            company_id, app_id, app_secret = result
            # Get new long-lived token
            new_token = get_new_long_lived_token(app_id, app_secret, access_token)
            
            if new_token:
                # Update token in database
                cursor.execute("""
                    UPDATE companies 
                    SET whatsapp_access_token = %s,
                        whatsapp_token_updated_at = NOW()
                    WHERE id = %s
                """, (new_token, company_id))
                mysql.connection.commit()
                
                # Retry with new token
                headers["Authorization"] = f"Bearer {new_token}"
                response = requests.post(url, headers=headers, json=data)
                response_json = response.json()
                logger.info("Message retry with new token response: %s", response_json)
                return response_json
    
    return response_json

def get_new_long_lived_token(app_id, app_secret, current_token):
    try:
        # Exchange for long-lived token
        response = requests.get(
            "https://graph.facebook.com/v19.0/oauth/access_token",
            params={
                "grant_type": "fb_exchange_token",
                "client_id": app_id,
                "client_secret": app_secret,
                "fb_exchange_token": current_token
            }
        )
        
        if response.status_code == 200:
            new_token = response.json().get("access_token")
            logger.info("Successfully obtained new long-lived token")
            return new_token
        else:
            logger.error(f"Failed to get long-lived token: {response.text}")
            return None
            
    except Exception as e:
        logger.error(f"Error getting long-lived token: {str(e)}")
        return None

# Add a new route to get chat history for admin panel
@whatsapp_bp.route('/chat-history/<company_id>', methods=['GET'])
def get_company_chat_history(company_id):
    try:
        customer_number = request.args.get('customer_number')
        if not customer_number:
            # If no customer number provided, get all conversations for the company
            cursor = mysql.connection.cursor()
            cursor.execute("""
                SELECT DISTINCT 
                    wc.customer_number,
                    wc.created_at as conversation_started,
                    wc.updated_at as last_message_at,
                    (SELECT content 
                     FROM whatsapp_messages 
                     WHERE company_id = wc.company_id 
                     AND customer_number = wc.customer_number 
                     ORDER BY created_at DESC 
                     LIMIT 1) as last_message
                FROM whatsapp_conversations wc
                WHERE wc.company_id = %s
                ORDER BY wc.updated_at DESC
            """, (company_id,))
            conversations = cursor.fetchall()
            return jsonify({
                "success": True,
                "conversations": [{
                    "customer_number": conv[0],
                    "started_at": conv[1].isoformat(),
                    "last_message_at": conv[2].isoformat(),
                    "last_message": conv[3]
                } for conv in conversations]
            })
        else:
            # Get chat history for specific customer
            chat_history = get_chat_history(company_id, customer_number)
            return jsonify({
                "success": True,
                "messages": [{
                    "role": msg[0],
                    "content": msg[1],
                    "timestamp": msg[2].isoformat()
                } for msg in chat_history]
            })
            
    except Exception as e:
        logger.error("Error getting chat history: %s", str(e))
        return jsonify({"success": False, "error": str(e)}), 500
