const express = require('express');
const twilio = require('twilio');
const axios = require('axios');
const Settings = require('../models/Settings');
const Agent = require('../models/Agent');
const Lead = require('../models/Lead');
const CallLog = require('../models/CallLog');
const { analyzeCallLog } = require('../utils/analyzer');
const { openRouterModel } = require('../utils/models');

const router = express.Router();

/**
 * Utility to replace variables in text
 */
function replaceVars(text, lead) {
    if (!text) return "";
    let personalized = text.replace(/{{name}}/g, lead.name || "there");

    // Check for company in fields
    const companyField = lead.fields?.find(f => f.name.toLowerCase() === 'company');
    const company = companyField?.value || "";
    personalized = personalized.replace(/{{company}}/g, company);

    // Replace any other fields if they exist in {{field_name}} format
    lead.fields?.forEach(f => {
        const regex = new RegExp(`{{${f.name}}}`, 'g');
        personalized = personalized.replace(regex, f.value);
    });

    return personalized;
}

/**
 * POST /api/twilio/voice
 * Initial webhook called when Twilio receives a call.
 */
router.post('/voice', async (req, res) => {
    const { userId, agentId, leadId, campaignId } = req.query;
    const VoiceResponse = twilio.twiml.VoiceResponse;
    const response = new VoiceResponse();
    const callSid = req.body.CallSid;

    try {
        if (!agentId || !leadId) {
            console.error('Missing agentId or leadId in voice webhook');
            response.say('System configuration error. Ending call.');
            response.hangup();
            return res.type('text/xml').send(response.toString());
        }

        const [agent, lead] = await Promise.all([
            Agent.findById(agentId),
            Lead.findById(leadId)
        ]);

        if (!agent || !lead) {
            console.error('Could not find Agent or Lead:', { agentId, leadId });
            response.say('System error. Agent or Lead record not found.');
            response.hangup();
            return res.type('text/xml').send(response.toString());
        }

        // Logic check: Custom Streaming (ElevenLabs) vs Standard (Twilio)
        if (agent.useCustomVoice) {
            const host = req.get('host');
            // Force WSS for any non-localhost connection (Twilio requirement)
            const protocol = (host.includes('localhost') || host.includes('127.0.0.1')) ? 'ws' : 'wss';
            const wsUrl = `${protocol}://${host}`;

            console.log(`📡 [Voice] Custom Voice Enabled. Connecting to Stream: ${wsUrl}`);
            const connect = response.connect();
            const stream = connect.stream({ url: wsUrl, name: 'Voice AI Stream' });

            // Pass metadata via Parameters instead of URL (more robust)
            stream.parameter({ name: 'userId', value: userId });
            stream.parameter({ name: 'agentId', value: agentId });
            stream.parameter({ name: 'leadId', value: leadId });
            stream.parameter({ name: 'campaignId', value: campaignId });
        } else {
            console.log(`🎙️ [Voice] Standard Voice Enabled. Using traditional TwiML loop.`);
            const personalizedGreeting = replaceVars(agent.openingMessage, lead);

            // Create/Update CallLog for tracking
            await CallLog.findOneAndUpdate(
                { callSid },
                {
                    campaignId, leadId, agentId, userId, callSid,
                    status: 'in-progress',
                    startTime: new Date(),
                    $setOnInsert: {
                        transcript: [
                            { role: 'system', content: agent.systemPrompt },
                            { role: 'assistant', content: personalizedGreeting }
                        ]
                    }
                },
                { upsert: true }
            );

            const gather = response.gather({
                input: 'speech',
                speechTimeout: 'auto',
                action: `/api/twilio/process?userId=${userId}&agentId=${agentId}&leadId=${leadId}&campaignId=${campaignId}`,
                method: 'POST',
            });

            gather.say({ voice: agent.voice || 'Polly.Amy' }, personalizedGreeting);

            // Loop if no input
            response.redirect(`/api/twilio/voice?userId=${userId}&agentId=${agentId}&leadId=${leadId}&campaignId=${campaignId}`);
        }

        res.type('text/xml').send(response.toString());
    } catch (err) {
        console.error('Twilio Voice Webhook Error:', err);
        response.say('Sorry, I encountered an error. Please try again later.');
        response.hangup();
        res.type('text/xml').send(response.toString());
    }
});

/**
 * POST /api/twilio/process
 * Webhook called after Gather detects speech.
 */
router.post('/process', async (req, res) => {
    const { userId, agentId, leadId, campaignId } = req.query;
    const speechResult = req.body.SpeechResult;
    const callSid = req.body.CallSid;
    const VoiceResponse = twilio.twiml.VoiceResponse;
    const response = new VoiceResponse();

    const redirectUrl = `/api/twilio/voice?userId=${userId}&agentId=${agentId}&leadId=${leadId}&campaignId=${campaignId}`;

    if (!speechResult) {
        response.redirect(redirectUrl);
        res.type('text/xml');
        return res.send(response.toString());
    }

    try {
        const [settings, agent, callLog] = await Promise.all([
            Settings.findOne({ userId }),
            Agent.findById(agentId),
            CallLog.findOne({ callSid })
        ]);

        if (!settings || !settings.openRouterKey || !agent || !callLog) {
            response.say('Configuration error. Please check your settings.');
            response.hangup();
            return res.type('text/xml').send(response.toString());
        }

        // Add user message to history
        const history = callLog.transcript.map(t => ({ role: t.role, content: t.content }));
        history.push({ role: "user", content: speechResult });

        // Call OpenRouter
        const llmResponse = await axios.post('https://openrouter.ai/api/v1/chat/completions', {
            model: openRouterModel,
            messages: history
        }, {
            headers: {
                'Authorization': `Bearer ${settings.openRouterKey}`,
                'Content-Type': 'application/json'
            }
        });

        const aiReply = llmResponse.data?.choices[0]?.message?.content || "I'm sorry, I couldn't process that.";

        // Update CallLog with user message and AI reply
        await CallLog.findOneAndUpdate(
            { callSid },
            {
                $push: {
                    transcript: [
                        { role: 'user', content: speechResult },
                        { role: 'assistant', content: aiReply }
                    ]
                }
            }
        );

        const gather = response.gather({
            input: 'speech',
            speechTimeout: 'auto',
            action: `/api/twilio/process?userId=${userId}&agentId=${agentId}&leadId=${leadId}&campaignId=${campaignId}`,
            method: 'POST',
        });

        gather.say({ voice: agent.voice || 'Polly.Amy' }, aiReply);

        // Keep loop alive
        response.redirect(redirectUrl);

        res.type('text/xml');
        res.send(response.toString());
    } catch (err) {
        console.error('LLM Processing Error:', err.message);
        response.say('Sorry, I am having trouble connecting to my brain. Please try again later.');
        response.hangup();
        res.type('text/xml');
        res.send(response.toString());
    }
});

/**
 * POST /api/twilio/status
 * Handle call status callbacks from Twilio
 */
router.post('/status', async (req, res) => {
    const { CallSid, CallStatus, CallDuration } = req.body;
    try {
        const updateData = {
            status: CallStatus,
            duration: CallDuration || 0,
            endTime: new Date()
        };

        if (req.body.RecordingUrl) {
            updateData.recordingUrl = req.body.RecordingUrl;
        }

        const updatedLog = await CallLog.findOneAndUpdate(
            { callSid: CallSid },
            updateData,
            { new: true }
        );

        if (updatedLog && updatedLog.campaignId) {
            const Campaign = require('../models/Campaign');
            const campaign = await Campaign.findById(updatedLog.campaignId);

            if (campaign) {
                const totalLeads = campaign.leadIds.length;
                const finishedCalls = await CallLog.countDocuments({
                    campaignId: updatedLog.campaignId,
                    status: { $in: ['completed', 'failed', 'busy', 'no-answer', 'canceled'] }
                });

                if (finishedCalls >= totalLeads) {
                    await Campaign.findByIdAndUpdate(updatedLog.campaignId, { status: 'completed' });
                    console.log(`[Status Webhook] Campaign ${updatedLog.campaignId} marked as completed.`);
                }
            }
        }

        // Automatic Call Analysis Trigger
        if (CallStatus === 'completed' && updatedLog) {
            const settings = await Settings.findOne({ userId: updatedLog.userId });
            if (settings?.autoAnalysisEnabled) {
                console.log(`[Status Webhook] Auto-analysis triggered for call ${CallSid}`);
                // Run in background (don't await to keep Twilio response fast)
                analyzeCallLog(updatedLog._id).catch(err => {
                    console.error(`[Status Webhook] Background analysis failed:`, err);
                });
            }
        }

        res.status(200).send('OK');
    } catch (err) {
        console.error('Twilio Status Callback Error:', err);
        res.status(500).send('Error');
    }
});

module.exports = router;
