<?php

namespace App\Http\Controllers\Twilio;

use App\Actions\Communications\HandleInboundCallAction;
use App\Models\Communication\Call;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Twilio\TwiML\VoiceResponse;

class TwilioController {
    public function __invoke(Request $request, HandleInboundCallAction $action): Response {
        Log::debug('Twilio Request', $request->all());

        if ($request->get('Direction') == 'inbound' && !str_starts_with($request->string('From')->toString(), 'client:')) {
            return $this->handleInbound($request, $action);
        }

        return $this->handleOutbound($request);
    }

    protected function handleInbound(Request $request, HandleInboundCallAction $action): Response {
        $from = $request->string('From')->toString();

        if (str_starts_with($from, 'client:')) {
            $voice = new VoiceResponse();
            $voice->say('Call configuration error. Please contact support.');
            return response((string)$voice, 200)->header('Content-Type', 'text/xml');
        }

        try {
            $twiml = $action->handle(
                fromNumber: $from,
                toNumber: $request->string('To')->toString(),
                payload: $request->all(),
            );

            return response($twiml, 200)->header('Content-Type', 'text/xml');
        } catch (\Throwable $e) {
            Log::error('TwilioController: failed to handle inbound call.', [
                'from' => $from,
                'to' => $request->input('To'),
                'error' => $e->getMessage(),
                'exception' => $e,
            ]);
        }

        $voice = new VoiceResponse();
        $voice->say('Sorry, we were unable to connect your call. Please try again later.');
        return response((string)$voice, 200)->header('Content-Type', 'text/xml');
    }

    protected function handleOutbound(Request $request): Response {
        $callId = (int) $request->input('call_id');

        Log::debug('TwilioController: handleOutbound', [
            'call_id'   => $callId,
            'CallSid'   => $request->input('CallSid'),
            'From'      => $request->input('From'),
            'Direction' => $request->input('Direction'),
            'params'    => $request->all(),
        ]);

        try {
            if (! $callId) {
                throw new \InvalidArgumentException('Missing call_id parameter in Twilio webhook request.');
            }

            /** @var Call $call */
            $call = Call::query()
                ->with(['communication', 'phoneNumber'])
                ->findOrFail($callId);

            $twilioCallSid = $request->input('CallSid');
            if ($twilioCallSid && ! $call->provider_call_sid) {
                $call->update(['provider_call_sid' => $twilioCallSid]);
            }

            $voice = new VoiceResponse();

            $dial = $voice->dial('', [
                'callerId'                        => $call->from_number,
                'record'                          => $call->is_recorded ? 'record-from-answer-dual' : 'do-not-record',
                'recordingStatusCallback'         => route('twilio.voice.recording'),
                'recordingStatusCallbackMethod'   => 'POST',
                'recordingStatusCallbackEvent'    => 'completed',
                'answerOnBridge'                  => true,
            ]);

            $dial->number($call->to_number, [
                'statusCallback'       => route('twilio.voice.status'),
                'statusCallbackMethod' => 'POST',
                // Twilio only accepts these four event names. busy/failed/
                // no-answer/canceled are CallStatus *values*, not events —
                // including them triggers warning 21626 and Twilio drops the
                // whole subscription back to its default (completed only),
                // costing us the initiated/ringing/answered transitions.
                'statusCallbackEvent'  => 'initiated ringing answered completed',
            ]);

            return response((string) $voice, 200)->header('Content-Type', 'text/xml');

        } catch (\Throwable $e) {
            Log::error('TwilioController: failed to handle outbound call.', [
                'call_id'   => $callId,
                'CallSid'   => $request->input('CallSid'),
                'error'     => $e->getMessage(),
                'exception' => $e,
            ]);

            $voice = new VoiceResponse();
            $voice->say('Sorry, we were unable to connect your call. Please try again.');
            return response((string) $voice, 200)->header('Content-Type', 'text/xml');
        }
    }
}