<?php

namespace App\Services;

use App\Models\Incident;
use App\Models\NotificationDelivery;
use App\Models\User;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * Voice call escalation for critical incidents that haven't been acknowledged
 * within a threshold time (configured per escalation policy).
 *
 * The flow:
 *
 *   1) Critical incident opens → notifications dispatch via WhatsApp/Telegram/email
 *   2) Escalation policy starts a timer (e.g., 5 minutes)
 *   3) If unacked within timer → this service places a phone call
 *   4) Call plays TTS message: "Critical alert on mail-02. Press 1 to ack,
 *      2 to escalate to next person, 3 to dismiss."
 *   5) Operator's response is captured via Twilio webhook → updates incident
 *
 * Catches sleeping engineers — chat notifications get muted at night, a ringing
 * phone doesn't.
 *
 * Configuration in .env:
 *   TWILIO_SID, TWILIO_TOKEN, TWILIO_FROM_NUMBER
 */
class VoiceCallService
{
    /**
     * Place a TTS voice call for an unacked critical incident.
     */
    public function callForIncident(Incident $incident, User $recipient): ?NotificationDelivery
    {
        if (!$recipient->phone) {
            Log::info("VoiceCall: skipping {$recipient->email} — no phone number");
            return null;
        }

        $sid = config('services.twilio.sid');
        $token = config('services.twilio.token');
        $from = config('services.twilio.from');

        if (!$sid || !$token || !$from) {
            Log::warning("VoiceCall: Twilio not configured");
            return null;
        }

        // Build the TwiML URL — Twilio fetches it to know what to say/do.
        // We host it ourselves so we can include the incident-specific message.
        $twimlUrl = url('/api/voice/twiml/' . $incident->id);
        $statusCallback = url('/api/voice/status/' . $incident->id);

        try {
            $response = Http::withBasicAuth($sid, $token)
                ->asForm()
                ->post("https://api.twilio.com/2010-04-01/Accounts/{$sid}/Calls.json", [
                    'To' => $recipient->phone,
                    'From' => $from,
                    'Url' => $twimlUrl,
                    'StatusCallback' => $statusCallback,
                    'Method' => 'POST',
                    'Timeout' => 30,
                ]);

            $payload = $response->json();

            $delivery = NotificationDelivery::create([
                'channel_key' => 'voice',
                'channel_provider' => 'twilio',
                'subject_type' => Incident::class,
                'subject_id' => $incident->id,
                'recipient_user_id' => $recipient->id,
                'recipient_address' => $recipient->phone,
                'status' => $response->successful() ? 'sent' : 'failed',
                'payload' => ['twiml_url' => $twimlUrl],
                'response' => $payload,
                'sent_at' => now(),
            ]);

            return $delivery;
        } catch (\Throwable $e) {
            Log::error("VoiceCall failed: " . $e->getMessage());
            return NotificationDelivery::create([
                'channel_key' => 'voice',
                'channel_provider' => 'twilio',
                'subject_type' => Incident::class,
                'subject_id' => $incident->id,
                'recipient_user_id' => $recipient->id,
                'recipient_address' => $recipient->phone,
                'status' => 'failed',
                'error_message' => $e->getMessage(),
                'failed_at' => now(),
            ]);
        }
    }

    /**
     * Returns the TwiML markup that Twilio plays to the recipient.
     * Twilio hits /api/voice/twiml/{incident} to fetch this.
     */
    public function buildTwiml(Incident $incident): string
    {
        $title = htmlspecialchars($incident->title, ENT_XML1);
        $severity = strtoupper($incident->severity);
        $subject = $incident->subject_id ? "subject ID {$incident->subject_id}" : 'general';

        $message = "{$severity} incident. {$title}. Affecting {$subject}. " .
            "Press 1 to acknowledge. Press 2 to escalate. Press 3 to dismiss.";

        return <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Gather numDigits="1" action="/api/voice/respond/{$incident->id}" method="POST" timeout="10">
        <Say voice="alice" language="en-US">{$message}</Say>
        <Say voice="alice" language="en-US">{$message}</Say>
    </Gather>
    <Say voice="alice">No response. Goodbye.</Say>
    <Hangup/>
</Response>
XML;
    }

    /**
     * Handle the operator's keypad response from Twilio's gather webhook.
     */
    public function handleResponse(Incident $incident, string $digit, ?User $caller = null): string
    {
        switch ($digit) {
            case '1':
                $incident->update([
                    'status' => 'acknowledged',
                    'acknowledged_at' => now(),
                    'acknowledged_by' => $caller?->id,
                ]);
                return $this->sayAndHangup("Acknowledged. Thank you.");

            case '2':
                // Trigger next step in escalation policy
                if ($incident->escalation_policy_id) {
                    \App\Jobs\AdvanceEscalation::dispatch($incident);
                }
                return $this->sayAndHangup("Escalating to the next person on call.");

            case '3':
                $incident->update(['suppressed_voice' => true]);
                return $this->sayAndHangup("Dismissed. No more voice calls for this incident.");

            default:
                return $this->sayAndHangup("Invalid response. Goodbye.");
        }
    }

    private function sayAndHangup(string $msg): string
    {
        $msg = htmlspecialchars($msg, ENT_XML1);
        return <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Say voice="alice" language="en-US">{$msg}</Say>
    <Hangup/>
</Response>
XML;
    }
}
