"""
Frontend router for simplified API integration
"""

import asyncio
import time
import httpx
import re
from fastapi import APIRouter, HTTPException, Depends, Request
from typing import Dict, Any, Optional, List
from sqlalchemy.orm import Session
from datetime import datetime

from models.frontend import FrontendChatRequest, FrontendChatResponse
from models.rag import RAGChatRequest, RAGChatResponse
from models.appointment import AppointmentRequest, MeetingIntentDetectionResponse
from models.user_chat_history import UserChatHistory
from core.dependencies import get_logger, get_conversation_handler, get_rag_crew, get_task_manager
from core.database import get_db
from utils.url_config import API_BASE_URL
from utils.appointment_state import (
    is_in_appointment_flow, get_missing_fields, get_extracted_info,
    start_appointment_flow, update_appointment_flow, complete_appointment_flow
)
from utils.frontend_integration import frontend_integration

router = APIRouter(prefix="/frontend", tags=["frontend"])


@router.post("/chat", response_model=FrontendChatResponse)
async def frontend_chat_endpoint(
    request: FrontendChatRequest,
    db: Session = Depends(get_db),
    conversation_handler=Depends(get_conversation_handler),
    logger=Depends(get_logger),
    rag_crew=Depends(get_rag_crew),
    task_manager=Depends(get_task_manager)
):
    """
    Combined endpoint for frontend chat integration.
    
    This endpoint:
    1. Accepts a chat message
    2. Processes it through the RAG system
    3. Optionally waits for completion (with timeout)
    4. Returns either the final result or a progress update
    
    Frontend developers only need to integrate with this single endpoint.
    """
    try:
        logger.info(f"Received frontend chat request: {request.message[:50]}..." if len(request.message) > 50 else request.message)

        # Extract conversation ID from userDetails (camelCase) or use the provided one
        conversation_id = request.userDetails.conversationId if hasattr(request, 'userDetails') else request.conversation_id

        # Check if conversation exists by trying to get its history
        history = conversation_handler.get_conversation_history(conversation_id)
        is_new_conversation = not history
        if is_new_conversation:
            # This is a new conversation with the provided ID
            conversation_handler.create_conversation(conversation_id)
            logger.info(f"New conversation with ID: {conversation_id[:8]}")
        else:
            logger.info(f"Continuing conversation with ID: {conversation_id[:8]}")
            
        # Check if we're in the middle of collecting appointment information
        in_appointment_flow = is_in_appointment_flow(conversation_id)
        
        # If we're in an appointment flow, handle it with enhanced response generator
        if is_in_appointment_flow(conversation_id):
            logger.info(f"Continuing appointment flow for conversation {conversation_id[:8]}")
            
            # Add user message to conversation history
            conversation_handler.add_user_message(
                conversation_id=conversation_id,
                content=request.message
            )
            
            # Get conversation history for context
            history = conversation_handler.get_conversation_history(conversation_id)
            
            # Use the enhanced response generator for appointment handling
            try:
                # Get empty results since we're handling appointment flow
                results = {"results": []}
                
                # Process the message with the frontend integration
                response_data = await frontend_integration.process_message(
                    message=request.message,
                    conversation_id=conversation_id,
                    retrieved_info=results,
                    conversation_history=history,
                    is_new_conversation=is_new_conversation
                )
                
                # Add the response to the conversation
                conversation_handler.add_assistant_message(
                    conversation_id=conversation_id,
                    content=response_data.get("response", "I'm processing your appointment request.")
                )
                
                # Return the response
                return FrontendChatResponse(
                    reply=response_data.get("response", "I'm processing your appointment request."),
                    conversation_id=conversation_id,
                    complete=True,
                    progress=100.0
                )
            except Exception as e:
                logger.error(f"Error using enhanced response generator: {str(e)}")
                # Continue with the existing flow if there's an error
            
            # Get the missing fields and extracted info from appointment state
            missing_fields = get_missing_fields(conversation_id)
            extracted_info = get_extracted_info(conversation_id)
            
            # Try to extract missing information from the new message
            updated_missing_fields = missing_fields.copy()
            
            # Try to extract name if missing
            if "name" in updated_missing_fields:
                name_match = re.search(r"(?:my name is|i am|i'm) ([\w\s]+)|([\w\s]+) is my name", request.message.lower())
                if name_match:
                    extracted_name = name_match.group(1) if name_match.group(1) else name_match.group(2)
                    extracted_info["name"] = extracted_name.strip().title()
                    updated_missing_fields.remove("name")
            
            # Try to extract email if missing
            if "email" in updated_missing_fields:
                email_match = re.search(r"[\w.+-]+@[\w-]+\.[\w.-]+", request.message.lower())
                if email_match:
                    extracted_info["email"] = email_match.group(0)
                    updated_missing_fields.remove("email")
            
            # Try to extract phone if missing
            if "phone" in updated_missing_fields:
                phone_match = re.search(r"(?:phone|contact|call)[:\s]*(\+?[\d\s-]{10,})|([\d\s-]{10,})", request.message.lower())
                if phone_match:
                    extracted_phone = phone_match.group(1) if phone_match.group(1) else phone_match.group(2)
                    extracted_info["phone"] = extracted_phone.strip()
                    updated_missing_fields.remove("phone")
            
            # Try to extract topic if missing
            if "topic" in updated_missing_fields:
                if len(request.message) > 5:  # If the message is substantial
                    extracted_info["topic"] = request.message[:100]  # Use the message as the topic
                    updated_missing_fields.remove("topic")
            
            # Update the appointment state with the new information
            update_appointment_flow(
                conversation_id=conversation_id,
                missing_fields=updated_missing_fields,
                extracted_info=extracted_info
            )
            logger.info(f"Updated appointment state with missing fields: {updated_missing_fields}")
            
            # If we still have missing fields, ask for them
            if updated_missing_fields:
                response_parts = ["Thank you for providing that information."]
                response_parts.append("I still need the following details to schedule your meeting:")
                
                for field in updated_missing_fields:
                    if field == "name":
                        response_parts.append("- Your full name")
                    elif field == "email":
                        response_parts.append("- Your email address for contact")
                    elif field == "phone":
                        response_parts.append("- Your phone number")
                    elif field == "topic":
                        response_parts.append("- The topic or purpose of the meeting")
                
                meeting_response = "\n".join(response_parts)
                
                # Add the response to the conversation
                conversation_handler.add_assistant_message(
                    conversation_id=conversation_id,
                    content=meeting_response
                )
                
                return FrontendChatResponse(
                    reply=meeting_response,
                    conversation_id=conversation_id,
                    complete=True,
                    progress=100.0,
                    task_id=None
                )
            
            # If we have all the necessary information, schedule the appointment
            else:
                # Create an appointment request
                appointment_request = {
                    "name": extracted_info.get("name", ""),
                    "email": extracted_info.get("email", ""),
                    "phone": extracted_info.get("phone", ""),
                    "company": extracted_info.get("company", ""),
                    "topic": extracted_info.get("topic", metadata.get("suggested_topic", "Consultation")),
                    "preferred_date": extracted_info.get("preferred_date"),
                    "preferred_time": extracted_info.get("preferred_time"),
                    "additional_notes": "",
                    "conversation_id": conversation_id
                }
                
                # Schedule the appointment
                try:
                    async with httpx.AsyncClient() as client:
                        appointment_response = await client.post(
                            f"{API_BASE_URL}/appointment/schedule",
                            json=appointment_request
                        )
                        
                        if appointment_response.status_code == 200:
                            appointment_data = appointment_response.json()
                            
                            # Generate a success message
                            success_message = f"Great! I've scheduled your appointment. Your appointment ID is {appointment_data['appointment_id']}. \n\nOur team will contact you soon at {extracted_info.get('email')} to confirm the details. Thank you for your interest in our services!"
                            
                            # Add the response to the conversation
                            conversation_handler.add_assistant_message(
                                conversation_id=conversation_id,
                                content=success_message
                            )
                            
                            # Update the appointment state to indicate it's scheduled
                            complete_appointment_flow(
                                conversation_id=conversation_id,
                                appointment_id=appointment_data['appointment_id']
                            )
                            logger.info(f"Appointment scheduled with ID: {appointment_data['appointment_id']}")
                            
                            return FrontendChatResponse(
                                reply=success_message,
                                conversation_id=conversation_id,
                                complete=True,
                                progress=100.0,
                                task_id=None
                            )
                        else:
                            error_text = await appointment_response.text()
                            logger.error(f"Error scheduling appointment: {error_text}")
                            
                            error_message = "I'm sorry, there was an error scheduling your appointment. Please try again later."
                            
                            # Add the error message to the conversation
                            conversation_handler.add_assistant_message(
                                conversation_id=conversation_id,
                                content=error_message
                            )
                            
                            return FrontendChatResponse(
                                reply=error_message,
                                conversation_id=conversation_id,
                                complete=True,
                                progress=100.0,
                                task_id=None
                            )
                except Exception as e:
                    logger.error(f"Error scheduling appointment: {str(e)}")
                    
                    error_message = "I'm sorry, there was an error scheduling your appointment. Please try again later."
                    
                    # Add the error message to the conversation
                    conversation_handler.add_assistant_message(
                        conversation_id=conversation_id,
                        content=error_message
                    )
                    
                    return FrontendChatResponse(
                        reply=error_message,
                        conversation_id=conversation_id,
                        complete=True,
                        progress=100.0,
                        task_id=None
                    )
        
        # Check if this is a new meeting request
        async with httpx.AsyncClient() as client:
            intent_response = await client.get(
                f"{API_BASE_URL}/appointment/detect-intent",
                params={"message": request.message}
            )
            
            if intent_response.status_code == 200:
                intent_data = intent_response.json()
                
                # If this is a meeting request with high confidence
                if intent_data.get("is_meeting_request", False) and intent_data.get("confidence", 0) > 0.5:
                    logger.info(f"Detected meeting request with confidence {intent_data.get('confidence')}")
                    
                    # Add user message to conversation history
                    conversation_handler.add_user_message(
                        conversation_id=conversation_id,
                        content=request.message
                    )                    
                    
                    # Check if we have all the necessary information
                    missing_fields = intent_data.get("missing_fields", [])
                    
                    # If we're missing information, ask for it
                    if missing_fields:
                        # Extract any information we already have
                        extracted_info = {}
                        
                        # Try to extract name
                        if "name" not in missing_fields:
                            name_match = re.search(r"(?:my name is|i am|i'm) ([\w\s]+)", request.message.lower())
                            if name_match:
                                extracted_info["name"] = name_match.group(1).strip().title()
                        
                        # Try to extract email
                        email_match = re.search(r"[\w.+-]+@[\w-]+\.[\w.-]+", request.message.lower())
                        if email_match:
                            extracted_info["email"] = email_match.group(0)
                            if "email" in missing_fields:
                                missing_fields.remove("email")
                        
                        # Try to extract phone
                        phone_match = re.search(r"(?:phone|contact|call)[:\s]*(\+?[\d\s-]{10,})", request.message.lower())
                        if phone_match:
                            extracted_info["phone"] = phone_match.group(1).strip()
                            if "phone" in missing_fields:
                                missing_fields.remove("phone")
                        
                        # Start the appointment flow with the detected information
                        start_appointment_flow(
                            conversation_id=conversation_id,
                            missing_fields=missing_fields,
                            extracted_info=extracted_info,
                            suggested_topic=intent_data.get('suggested_topic')
                        )
                        logger.info(f"Started appointment flow with missing fields: {missing_fields}")
                        
                        # Generate a response asking for the missing information
                        response_parts = [
                            "I'd be happy to schedule a meeting with our senior consultant team to discuss your needs."
                        ]
                        
                        if missing_fields:
                            response_parts.append("To proceed with scheduling, I'll need the following information:")
                            for field in missing_fields:
                                if field == "name":
                                    response_parts.append("- Your full name")
                                elif field == "email":
                                    response_parts.append("- Your email address for contact")
                                elif field == "phone":
                                    response_parts.append("- Your phone number")
                                elif field == "topic":
                                    response_parts.append("- The topic or purpose of the meeting")
                        
                        # Add a note about the information we already have
                        if extracted_info:
                            response_parts.append("\nI've noted the following information:")
                            for key, value in extracted_info.items():
                                response_parts.append(f"- {key.title()}: {value}")
                        
                        meeting_response = " \n".join(response_parts)
                        
                        # Add the response to the conversation
                        conversation_handler.add_assistant_message(
                            conversation_id=conversation_id,
                            content=meeting_response
                        )
                        
                        return FrontendChatResponse(
                            reply=meeting_response,
                            conversation_id=conversation_id,
                            complete=True,
                            progress=100.0,
                            task_id=None
                        )
                    
                    # If we have all the necessary information, schedule the appointment
                    else:
                        # This would be handled in a follow-up message after collecting all info
                        pass
        
        # Add user message to conversation history
        conversation_handler.add_user_message(
            conversation_id=conversation_id,
            content=request.message
        )

        try:
            # Get agent names for progress tracking
            agent_names = rag_crew.agent_names

            # Create an async task for the agent processing with agent names for progress tracking
            task_id = await task_manager.create_task(
                rag_crew.process_query(request.message, conversation_id, is_new_conversation),
                agent_names=agent_names
            )

            logger.info(f"Created task {task_id[:8]} for processing query in conversation {conversation_id[:8]}")

            # If wait_for_response is False, return immediately with the task ID
            if not request.wait_for_response:
                return FrontendChatResponse(
                    reply="Your request is being processed. Please check back for the complete response.",
                    conversation_id=conversation_id,
                    complete=False,
                    progress=0.0,
                    task_id=task_id
                )

            # Otherwise, poll for the result with the specified timeout
            timeout = request.timeout or 30  # Default to 30 seconds if not specified
            start_time = time.time()

            while time.time() - start_time < timeout:
                # Check task status directly using task_manager
                task_status = task_manager.get_task_status(task_id)

                if not task_status:
                    raise HTTPException(status_code=404, detail=f"Task {task_id} not found")

                if task_status["status"] == "completed":
                    # Task is complete, return the result
                    result = task_status.get("result", {})
                    return FrontendChatResponse(
                        reply=result.get("response", "No response generated"),
                        conversation_id=conversation_id,
                        complete=True,
                        progress=100.0,
                        task_id=None  # No need to return task ID for completed tasks
                    )
                elif task_status["status"] == "failed":
                    # Task failed, return error
                    error_message = task_status.get("error", "Unknown error")
                    return FrontendChatResponse(
                        reply=f"Error processing your request: {error_message}",
                        conversation_id=conversation_id,
                        complete=True,  # Mark as complete even though it failed
                        progress=100.0,
                        task_id=None
                    )

                # Task is still running, get progress
                progress = task_status.get("progress", 0.0)

                # If we're close to timeout, return what we have
                if time.time() - start_time > timeout - 1:
                    return FrontendChatResponse(
                        reply="Your request is still being processed. Please try again for the complete response.",
                        conversation_id=conversation_id,
                        complete=False,
                        progress=progress,
                        task_id=task_id
                    )

                # Wait before checking again
                await asyncio.sleep(0.5)  # Reduced sleep time for faster polling

            # If we get here, we've timed out
            return FrontendChatResponse(
                reply="Request timed out. Your message is still being processed.",
                conversation_id=conversation_id,
                complete=False,
                progress=task_status.get("progress", 0.0),
                task_id=task_id
            )
        except Exception as e:
            # Save error information to chat history
            try:
                # Check if there's already a chat history record for this conversation
                existing_chat = db.query(UserChatHistory).filter(
                    UserChatHistory.conversation_id == conversation_id
                ).order_by(UserChatHistory.id.desc()).first()

                if existing_chat:
                    # Append error information to the existing history log
                    error_entry = {
                        "timestamp": datetime.utcnow().isoformat(),
                        "type": "error",
                        "error_message": str(e),
                        "error_type": type(e).__name__,
                        "user_message": request.message,
                        "user_details": {
                            "name": getattr(request.userDetails, 'name', 'Unknown') if hasattr(request, 'userDetails') else 'Unknown',
                            "email": getattr(request.userDetails, 'email', None) if hasattr(request, 'userDetails') else None
                        }
                    }
                    existing_chat.history_log.append(error_entry)
                    db.commit()
                    logger.info(f"Updated existing chat history with error for conversation {conversation_id[:8]}")
                else:
                    # Create new chat history record with error information
                    user_chat = UserChatHistory(
                        conversation_id=conversation_id,
                        history_log=[{
                            "timestamp": datetime.utcnow().isoformat(),
                            "type": "error",
                            "error_message": str(e),
                            "error_type": type(e).__name__,
                            "user_message": request.message,
                            "user_details": {
                                "name": getattr(request.userDetails, 'name', 'Unknown') if hasattr(request, 'userDetails') else 'Unknown',
                                "email": getattr(request.userDetails, 'email', None) if hasattr(request, 'userDetails') else None
                            }
                        }]
                    )
                    db.add(user_chat)
                    db.commit()
                    logger.info(f"Created new chat history record with error for conversation {conversation_id[:8]}")
            except Exception as db_error:
                logger.error(f"Failed to save error to chat history: {str(db_error)}")

            logger.error(f"Error in frontend_chat_endpoint processing: {str(e)}")
            return FrontendChatResponse(
                reply=f"I'm sorry, I encountered an error while processing your request: {str(e)}",
                conversation_id=conversation_id,
                complete=True,
                progress=100.0,
                task_id=None
            )
    except Exception as e:
        logger.error(f"Error in frontend_chat_endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))
