<?php

namespace App\Actions\Communications;

use App\Models\Communication\SolicitorPhoneNumber;
use App\Models\Solicitor\Solicitor;
use App\Models\User\User;
use App\Services\Communications\TwilioClientIdentity;
use Twilio\Jwt\AccessToken;
use Twilio\Jwt\Grants\VoiceGrant;

class GenerateVoiceTokenAction
{
    public function handle(User $user): array
    {
        $identity = TwilioClientIdentity::for($user->id);

        // Source the account + outgoing TwiML app from the active *workspace*
        // solicitor's primary voice number so each solicitor/subaccount drives
        // its own dialler routing. We scope by the workspace (app(Solicitor))
        // rather than the user's own solicitor_id, because platform agents have
        // solicitor_id = null but operate inside a selected workspace — keying
        // off the user would miss the number and fall back to the env app SID
        // (which on staging is the prod app, routing calls to production).
        // API key/secret stay on the env (the shared master credentials that
        // sign the JWT), matching TransferCallAction::redirectCall.
        $phoneNumber = $this->resolvePhoneNumber();

        $accountSid  = $phoneNumber?->twilio_subaccount_sid
            ?: config('services.twilio.sid');
        $twimlAppSid = $phoneNumber?->twilio_twiml_app_sid
            ?: config('services.twilio.twiml_app_sid');

        // Make the env fallback LOUD. A null workspace number silently routes
        // through the env TwiML app — which on non-prod is the prod app, i.e.
        // a staging dialler placing real calls on production. Never let that
        // happen quietly again: if the app SID didn't come from the workspace
        // number, log exactly why so it's diagnosable in seconds, not a day.
        if (! $phoneNumber?->twilio_twiml_app_sid) {
            logger()->warning('Dialler token fell back to env TwiML app SID (no workspace-configured app).', [
                'workspace_solicitor_id'   => app(Solicitor::class)?->id,
                'resolved_phone_number_id' => $phoneNumber?->id,
                'env_twiml_app_sid'        => $twimlAppSid,
                'identity'                 => $identity,
            ]);
        }

        $apiKey    = config('services.twilio.api_key');
        $apiSecret = config('services.twilio.api_secret');

        $tokenTtl = config('services.twilio.token_ttl', 3600);

        $accessToken = new AccessToken(
            $accountSid,
            $apiKey,
            $apiSecret,
            $tokenTtl,
            $identity
        );

        $voiceGrant = new VoiceGrant();
        $voiceGrant->setOutgoingApplicationSid($twimlAppSid);
        $voiceGrant->setIncomingAllow(true);

        $accessToken->addGrant($voiceGrant);

        return [
            'identity' => $identity,
            'token' => $accessToken->toJWT(),
            'expires_in' => $tokenTtl,
        ];
    }

    /**
     * The active workspace solicitor's primary, active, voice-capable number.
     * Falls back to the first active voice number when none is flagged primary;
     * returns null when no workspace is bound or it has no voice number, in
     * which case the caller uses the system-wide env credentials.
     */
    protected function resolvePhoneNumber(): ?SolicitorPhoneNumber
    {
        $solicitor = app(Solicitor::class);

        if (! $solicitor) {
            return null;
        }

        return SolicitorPhoneNumber::query()
            ->where('solicitor_id', $solicitor->id)
            ->where('is_active', true)
            ->whereJsonContains('capabilities', 'voice')
            ->orderByDesc('is_primary')
            ->orderBy('id')
            ->first();
    }
}
