<?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\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';

        // File a Document against the record only when the call is tied to a
        // Client — unmatched callers have nowhere to file it, but the recording
        // (and its transcript) are still stored above.
        if ($communicable instanceof Client) {
            // Resolve the "Call Recording" document type, tolerating a race
            // condition when multiple recording jobs run concurrently.
            try {
                $docType = DocumentType::firstOrCreate(['display_name' => 'Call Recording']);
            } catch (\Illuminate\Database\QueryException) {
                $docType = DocumentType::where('display_name', 'Call Recording')->firstOrFail();
            }

            $label       = $isVoicemail ? 'Voicemail' : 'Call Recording';
            $displayName = $label . ' – ' . ($recording->recorded_at ?? now())->format('d M Y H:i');

            $communicable->documents()->create([
                'solicitor_id' => $communicable->solicitor_id,
                'user_id'      => null,
                'display_name' => $displayName,
                'file_name'    => $fileName,
                'file_type'    => 'audio/mpeg',
                'file_size'    => mb_strlen($audio, '8bit'),
                'type_id'      => $docType->id,
            ]);
        }

        // 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) {
            Notifications::sendToRole('deeplinx', new VoicemailReceivedNotification($recording));
        }
    }
}
