"""
WhatsApp Voice Integration using Twilio
Handles both inbound and outbound voice calls through WhatsApp
"""

from fastapi import FastAPI, Request, Response, BackgroundTasks
from fastapi.responses import PlainTextResponse
from twilio.rest import Client
from twilio.twiml.voice_response import VoiceResponse, Gather
import asyncio
import httpx
from typing import Dict, Any, Optional, List
from datetime import datetime
import os
from enum import Enum

class CallDirection(str, Enum):
    INBOUND = "inbound"
    OUTBOUND = "outbound"

class CallState(str, Enum):
    INITIATED = "initiated"
    RINGING = "ringing"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"

class WhatsAppVoiceAgent:
    """
    WhatsApp voice call handler using Twilio
    """
    
    def __init__(
        self,
        account_sid: str,
        auth_token: str,
        whatsapp_number: str,
        api_base_url: str = "http://localhost:8000"
    ):
        self.client = Client(account_sid, auth_token)
        self.whatsapp_number = whatsapp_number
        self.api_base_url = api_base_url
        self.active_calls: Dict[str, Dict[str, Any]] = {}
    
    async def initiate_outbound_call(
        self,
        to_phone: str,
        campaign_type: str,
        metadata: Dict[str, Any] = None
    ) -> str:
        """
        Initiate outbound WhatsApp call
        
        Args:
            to_phone: Recipient phone number (E.164 format)
            campaign_type: Type of campaign (payment_reminder, loan_offer, etc.)
            metadata: Additional context data
            
        Returns:
            call_sid: Twilio call SID
        """
        # Get user context from API
        user_context = await self._fetch_user_context(to_phone)
        
        # Determine greeting based on campaign type
        greeting = self._get_campaign_greeting(
            campaign_type, 
            user_context.get("name", ""),
            user_context.get("language_preference", "swahili")
        )
        
        # Create TwiML for initial message
        twiml = VoiceResponse()
        
        # Initial greeting
        twiml.say(
            greeting,
            voice='Polly.Aditi',  # Supports multiple languages
            language='sw'  # Swahili
        )
        
        # Gather user response
        gather = Gather(
            input='speech',
            timeout=5,
            action=f'/voice/process',
            method='POST',
            language='sw-TZ'  # Swahili (Tanzania)
        )
        
        gather.say(
            "Bonyeza namba yoyote au sema jibu lako.",
            voice='Polly.Aditi',
            language='sw'
        )
        
        twiml.append(gather)
        
        # Make the call
        call = self.client.calls.create(
            to=f'whatsapp:{to_phone}',
            from_=f'whatsapp:{self.whatsapp_number}',
            twiml=str(twiml),
            status_callback=f'{self.api_base_url}/voice/status',
            status_callback_event=['initiated', 'ringing', 'answered', 'completed']
        )
        
        # Store call context
        self.active_calls[call.sid] = {
            "call_sid": call.sid,
            "phone": to_phone,
            "direction": CallDirection.OUTBOUND,
            "campaign_type": campaign_type,
            "user_context": user_context,
            "metadata": metadata or {},
            "state": CallState.INITIATED,
            "started_at": datetime.now()
        }
        
        return call.sid
    
    def _get_campaign_greeting(
        self, 
        campaign_type: str, 
        name: str,
        language: str = "swahili"
    ) -> str:
        """
        Get appropriate greeting based on campaign type and language
        """
        greetings = {
            "swahili": {
                "payment_reminder": f"Habari {name}, hii ni Credable. Tunakukumbusha kuhusu malipo yako ya mkopo.",
                "loan_offer": f"Habari {name}, hii ni Credable. Tuna mkopo mpya kwako.",
                "survey": f"Habari {name}, hii ni Credable. Je, tunaweza kuzungumza dakika chache?",
                "default": f"Habari {name}, hii ni Credable. Tunakupigia simu."
            },
            "english": {
                "payment_reminder": f"Hello {name}, this is Credable. We're calling about your loan payment.",
                "loan_offer": f"Hello {name}, this is Credable. We have a new loan offer for you.",
                "survey": f"Hello {name}, this is Credable. Can we talk for a few minutes?",
                "default": f"Hello {name}, this is Credable calling."
            }
        }
        
        return greetings.get(language, greetings["swahili"]).get(
            campaign_type, 
            greetings.get(language, greetings["swahili"])["default"]
        )
    
    async def _fetch_user_context(self, phone: str) -> Dict[str, Any]:
        """
        Fetch user context from API
        """
        async with httpx.AsyncClient() as client:
            try:
                response = await client.get(
                    f"{self.api_base_url}/api/v1/users/profile",
                    params={"phone": phone}
                )
                response.raise_for_status()
                return response.json()
            except Exception as e:
                print(f"Error fetching user context: {e}")
                return {
                    "phone": phone,
                    "name": "Customer",
                    "language_preference": "swahili"
                }
    
    async def update_call_state(
        self, 
        call_sid: str, 
        state: CallState,
        metadata: Dict[str, Any] = None
    ):
        """
        Update call state and metadata
        """
        if call_sid in self.active_calls:
            self.active_calls[call_sid]["state"] = state
            self.active_calls[call_sid]["updated_at"] = datetime.now()
            
            if metadata:
                self.active_calls[call_sid]["metadata"].update(metadata)


# FastAPI app for WhatsApp voice webhooks
app = FastAPI(title="WhatsApp Voice Agent Webhooks")

# Initialize agent (get from env vars in production)
ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID", "your_account_sid")
AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN", "your_auth_token")
WHATSAPP_NUMBER = os.getenv("TWILIO_WHATSAPP_NUMBER", "+14155238886")

agent = WhatsAppVoiceAgent(ACCOUNT_SID, AUTH_TOKEN, WHATSAPP_NUMBER)

@app.post("/voice/inbound")
async def handle_inbound_call(request: Request):
    """
    Handle incoming WhatsApp voice call
    """
    form_data = await request.form()
    from_number = form_data.get("From", "").replace("whatsapp:", "")
    call_sid = form_data.get("CallSid")
    
    print(f"📞 Inbound call from {from_number}")
    
    # Fetch user context
    user_context = await agent._fetch_user_context(from_number)
    
    # Store call info
    agent.active_calls[call_sid] = {
        "call_sid": call_sid,
        "phone": from_number,
        "direction": CallDirection.INBOUND,
        "user_context": user_context,
        "state": CallState.IN_PROGRESS,
        "started_at": datetime.now()
    }
    
    # Create TwiML response
    response = VoiceResponse()
    
    # Greeting
    name = user_context.get("name", "Customer")
    language = user_context.get("language_preference", "swahili")
    
    if language == "swahili":
        greeting = f"Karibu {name}. Ninaweza kukusaidia aje leo?"
    else:
        greeting = f"Hello {name}. How can I help you today?"
    
    response.say(
        greeting,
        voice='Polly.Aditi',
        language='sw' if language == "swahili" else 'en'
    )
    
    # Gather user input
    gather = Gather(
        input='speech',
        timeout=5,
        action='/voice/process',
        method='POST',
        language='sw-TZ' if language == "swahili" else 'en-US'
    )
    
    gather.say(
        "Niambie unahitaji nini.",
        voice='Polly.Aditi',
        language='sw' if language == "swahili" else 'en'
    )
    
    response.append(gather)
    
    # If no input, end call
    response.say("Asante kwa kupiga simu. Kwa heri!", voice='Polly.Aditi', language='sw')
    
    return PlainTextResponse(str(response), media_type="text/xml")

@app.post("/voice/process")
async def process_user_input(request: Request):
    """
    Process user speech input and generate response
    """
    form_data = await request.form()
    call_sid = form_data.get("CallSid")
    speech_result = form_data.get("SpeechResult", "")
    
    print(f"🗣️ User said: {speech_result}")
    
    # Get call context
    call_context = agent.active_calls.get(call_sid, {})
    user_context = call_context.get("user_context", {})
    
    # Process with LLM/NLU to determine intent
    intent = await classify_intent(speech_result)
    
    # Create response based on intent
    response = VoiceResponse()
    
    if intent == "loan_balance":
        # Fetch loan info
        phone = user_context.get("phone")
        loan_info = await fetch_loan_balance(phone)
        
        # Generate response
        if user_context.get("language_preference") == "swahili":
            response_text = f"Kiasi chako cha mkopo ni shilingi {loan_info['outstanding_balance']:,.0f}. "
            response_text += f"Tarehe ya kulipa ni {loan_info['due_date']}."
        else:
            response_text = f"Your loan balance is {loan_info['outstanding_balance']:,.0f} shillings. "
            response_text += f"Due date is {loan_info['due_date']}."
        
        response.say(response_text, voice='Polly.Aditi', language='sw')
    
    elif intent == "payment_status":
        # Fetch payment info
        phone = user_context.get("phone")
        payments = await fetch_recent_payments(phone)
        
        if payments:
            last_payment = payments[0]
            response_text = f"Malipo yako ya mwisho yalikuwa shilingi {last_payment['amount']:,.0f} tarehe {last_payment['payment_date']}."
        else:
            response_text = "Hakuna malipo yaliyopokelewa hivi karibuni."
        
        response.say(response_text, voice='Polly.Aditi', language='sw')
    
    else:
        # General response
        response.say(
            "Samahani, sijaeleweka vizuri. Je, unaweza kurudia?",
            voice='Polly.Aditi',
            language='sw'
        )
    
    # Ask if they need anything else
    gather = Gather(
        input='speech',
        timeout=5,
        action='/voice/process',
        method='POST',
        language='sw-TZ'
    )
    
    gather.say(
        "Je, kuna kitu kingine unachohitaji?",
        voice='Polly.Aditi',
        language='sw'
    )
    
    response.append(gather)
    
    # End call option
    response.say("Asante. Kwa heri!", voice='Polly.Aditi', language='sw')
    
    return PlainTextResponse(str(response), media_type="text/xml")

@app.post("/voice/status")
async def call_status_callback(request: Request):
    """
    Handle call status updates from Twilio
    """
    form_data = await request.form()
    call_sid = form_data.get("CallSid")
    call_status = form_data.get("CallStatus")
    
    print(f"📊 Call {call_sid}: {call_status}")
    
    # Map Twilio status to our status
    status_mapping = {
        "initiated": CallState.INITIATED,
        "ringing": CallState.RINGING,
        "in-progress": CallState.IN_PROGRESS,
        "completed": CallState.COMPLETED,
        "failed": CallState.FAILED,
        "busy": CallState.FAILED,
        "no-answer": CallState.FAILED
    }
    
    state = status_mapping.get(call_status, CallState.IN_PROGRESS)
    
    await agent.update_call_state(call_sid, state)
    
    return {"status": "ok"}

# Helper functions
async def classify_intent(text: str) -> str:
    """
    Simple intent classification (replace with actual NLU/LLM)
    """
    text_lower = text.lower()
    
    if any(word in text_lower for word in ['kiasi', 'balance', 'mkopo', 'deni']):
        return "loan_balance"
    elif any(word in text_lower for word in ['malipo', 'payment', 'kulipa', 'lipa']):
        return "payment_status"
    elif any(word in text_lower for word in ['historia', 'history', 'zamani']):
        return "loan_history"
    else:
        return "general"

async def fetch_loan_balance(phone: str) -> Dict[str, Any]:
    """
    Fetch loan balance from API
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            "http://localhost:8000/api/v1/loans/active",
            params={"phone": phone}
        )
        loans = response.json()
        
        if loans:
            loan = loans[0]
            balance_response = await client.get(
                f"http://localhost:8000/api/v1/loans/{loan['loan_id']}/balance"
            )
            return balance_response.json()
        
        return {"outstanding_balance": 0, "due_date": "N/A"}

async def fetch_recent_payments(phone: str) -> List[Dict[str, Any]]:
    """
    Fetch recent payments from API
    """
    async with httpx.AsyncClient() as client:
        response = await client.get(
            "http://localhost:8000/api/v1/payments/recent",
            params={"phone": phone, "limit": 5}
        )
        return response.json()

# Outbound calling endpoint
@app.post("/voice/outbound/trigger")
async def trigger_outbound_call(
    phone: str,
    campaign_type: str,
    background_tasks: BackgroundTasks
):
    """
    Trigger an outbound call
    """
    try:
        call_sid = await agent.initiate_outbound_call(
            to_phone=phone,
            campaign_type=campaign_type
        )
        
        return {
            "status": "success",
            "call_sid": call_sid,
            "message": f"Call initiated to {phone}"
        }
    except Exception as e:
        return {
            "status": "error",
            "message": str(e)
        }

if __name__ == "__main__":
    import uvicorn
    print("📱 Starting WhatsApp Voice Agent...")
    print("🔗 Webhook endpoints:")
    print("   - Inbound: /voice/inbound")
    print("   - Process: /voice/process")
    print("   - Status: /voice/status")
    print("   - Outbound: /voice/outbound/trigger")
    uvicorn.run(app, host="0.0.0.0", port=8001)
