<?php

namespace App\Jobs;

use App\Facades\Notifications;
use App\Jobs\Synapse\AutoTranscribeRecordingJob;
use App\Models\Client\Client;
use App\Models\Communication\Recording;
use App\Models\Document\DocumentType;
use App\Models\User\User;
use App\Notifications\VoicemailReceivedNotification;
use App\Services\Communications\Drivers\TwilioVoiceDriver;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;

class DownloadAndStoreRecordingJob implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $timeout = 600;
    public int $backoff = 60;

    public function __construct(public readonly int $recordingId) {}

    public function handle(TwilioVoiceDriver $twilio): void {
        $recording = Recording::with([
            'communication.communicable',
            'call.phoneNumber',
            'call.assignedAgent',
        ])->find($this->recordingId);

        if (!$recording) {
            Log::warning("DownloadAndStoreRecordingJob: recording {$this->recordingId} not found.");
            return;
        }

        // Skip if we already stored this file.
        if ($recording->hasStoredFile()) {
            return;
        }

        if (!$recording->recording_url) {
            Log::warning("DownloadAndStoreRecordingJob: recording {$this->recordingId} has no URL.");
            return;
        }

        // Download the audio from Twilio using the subaccount SID where available.
        // This runs regardless of who (if anyone) the call is tied to: audio
        // playback and transcription must work even for calls whose communicable
        // is not a Client — most importantly first-contact "initial" inbound calls
        // from a number that isn't matched to a client record yet.
        $subaccountSid = $recording->call?->phoneNumber?->twilio_subaccount_sid;
        $audio = $twilio->downloadRecording($recording->recording_url, $subaccountSid);

        if ($audio === null) {
            Log::error("DownloadAndStoreRecordingJob: failed to download recording {$this->recordingId}.");
            $this->fail(new \RuntimeException("Failed to download Twilio recording {$recording->provider_recording_sid}."));
            return;
        }

        // Store under the communicable's own document folder when it has one
        // (Client / Claim / DSAR), otherwise a solicitor-scoped recordings folder
        // so the file is always retrievable by RecordingStreamController.
        $communicable = $recording->communication?->communicable;

        $fileName = 'call-recording-' . $recording->provider_recording_sid . '.mp3';
        $basePath = ($communicable && method_exists($communicable, 'getDocumentStoragePath'))
            ? rtrim($communicable->getDocumentStoragePath(), '/') . '/recordings'
            : 'solicitors/' . $recording->solicitor_id . '/call-recordings';
        $storagePath = $basePath . '/' . $fileName;

        Storage::disk('public')->put($storagePath, $audio);

        $isVoicemail = $recording->communication?->status === 'voicemail';

        // Update the Recording so hasStoredFile() returns true and the Twilio
        // URL is no longer the only source of playback.
        $recording->update([
            'storage_disk' => 'public',
            'storage_path' => $storagePath,
        ]);

        AutoTranscribeRecordingJob::dispatch($recording->id);

        if ($isVoicemail) {
            $this->alertClientHandlers($recording, $communicable);
        }
    }

    /**
     * Notify every case handler assigned to the caller's client of the new
     * voicemail. The callback task is raised at the point the call is missed,
     * rather than waiting for this queued audio-download job.
     * No-op when the call is not tied to a Client or the client has no handlers.
     *
     * Safe against Twilio's repeated recording callbacks: the hasStoredFile()
     * guard at the top of handle() short-circuits every run after the first
     * download, so this runs once per voicemail.
     */
    protected function alertClientHandlers(
        Recording $recording,
        mixed $communicable,
    ): void {
        if (! $communicable instanceof Client) {
            return;
        }

        $handlers = User::whereIn(
            'id',
            $communicable->handlers()->pluck('user_id')->filter()->unique(),
        )->get();

        if ($handlers->isEmpty()) {
            return;
        }

        Notifications::sendToMany($handlers, new VoicemailReceivedNotification($recording));
    }
}
