<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use App\Models\ConversationModel;
use App\Models\LeadModel;

/**
 * VoiceAgentService
 *
 * Mirrors ChatbotService's structure but for phone calls: every caller
 * turn goes through handleSpeechInput(), builds on the same lead
 * capture / booking-intent / KB-FAQ / escalation logic, and returns
 * TwiML (via TwilioClient) instead of a chat reply string. Every call
 * — inbound or outbound — becomes a real `conversations` row keyed by
 * the Twilio Call SID, with the full turn-by-turn transcript in
 * `conversation_messages` AND a flattened `transcript_text` for quick
 * reading/AI summarization.
 */
final class VoiceAgentService
{
    public function __construct(
        private readonly TwilioClient $twilio = new TwilioClient(),
        private readonly ConversationModel $conversations = new ConversationModel(),
        private readonly LeadModel $leads = new LeadModel(),
        private readonly ChatbotService $textBrain = new ChatbotService()
    ) {
    }

    /** Called from the Twilio "incoming call" webhook. Returns TwiML. */
    public function handleIncomingCall(string $callSid, string $fromNumber, int $businessLineId, string $gatherActionUrl): string
    {
        $conversationId = $this->conversations->start('voice_agent', $businessLineId, $fromNumber, $callSid, 'inbound');
        $greeting = "Thanks for calling A&B. I'm an automated assistant — how can I help you today?";
        $this->conversations->addMessage($conversationId, 'bot', $greeting);

        return $this->twilio->buildGatherResponse($greeting, $gatherActionUrl . '?conversation_id=' . $conversationId);
    }

    /** Called from the Twilio "gather result" webhook with what the caller said (speech-to-text, done by Twilio itself). Returns TwiML. */
    public function handleSpeechInput(int $conversationId, string $speechText, int $businessLineId, string $gatherActionUrl): string
    {
        $result = $this->textBrain->handleMessage($conversationId, $speechText, $businessLineId);

        if ($result['escalated']) {
            return $this->twilio->buildSayAndHangup($result['reply']);
        }

        return $this->twilio->buildGatherResponse($result['reply'], $gatherActionUrl . '?conversation_id=' . $conversationId);
    }

    /** Called from the Twilio call-status-callback webhook when a call ends. */
    public function recordCallEnd(string $callSid, string $status, ?string $recordingUrl, ?int $durationSeconds): void
    {
        $conversation = $this->conversations->findByExternalRef($callSid);
        if ($conversation === null) {
            return;
        }

        $messages = $this->conversations->messages((int) $conversation['id']);
        $transcript = implode("\n", array_map(fn ($m) => ucfirst($m['sender']) . ': ' . $m['message'], $messages));

        $this->conversations->update((int) $conversation['id'], [
            'recording_url' => $recordingUrl,
            'duration_seconds' => $durationSeconds,
            'transcript_text' => $transcript,
            'ended_at' => now(),
        ]);

        if ($conversation['outcome'] === null) {
            $outcome = $conversation['related_lead_id'] !== null ? 'lead_created' : 'resolved';
            $this->conversations->end((int) $conversation['id'], $outcome);
        }
    }

    /** Staff-initiated outbound call — e.g. "call this lead back." */
    public function initiateOutboundCall(string $toNumber, int $businessLineId, ?int $leadId, string $twimlWebhookUrl): array
    {
        if (!$this->twilio->isConfigured()) {
            return ['success' => false, 'error' => 'Twilio is not connected. Configure it from Admin > Integrations.'];
        }

        $result = $this->twilio->initiateCall($toNumber, $twimlWebhookUrl);

        if (!$result['success']) {
            return $result;
        }

        $conversationId = $this->conversations->start('voice_agent', $businessLineId, $toNumber, $result['call_sid'], 'outbound');

        if ($leadId !== null) {
            Database::connection()->prepare('UPDATE conversations SET related_lead_id = :lead WHERE id = :id')
                ->execute(['lead' => $leadId, 'id' => $conversationId]);
        }

        return ['success' => true, 'call_sid' => $result['call_sid'], 'conversation_id' => $conversationId];
    }
}
