<?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 $backoff = 30;

    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;
        }

        // The communicable on the communication must be a Client.
        $client = $recording->communication?->communicable;
        if (!$client instanceof Client) {
            Log::warning("DownloadAndStoreRecordingJob: recording {$this->recordingId} communicable is not a Client.");
            return;
        }

        // Download the audio from Twilio using the subaccount SID where available.
        $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;
        }

        // Build a stable, unique file name.
        $fileName = 'call-recording-' . $recording->provider_recording_sid . '.mp3';
        $storagePath = rtrim($client->getDocumentStoragePath(), '/') . '/recordings/' . $fileName;

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

        // 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();
        }

        // Determine a human-readable name based on whether this is a voicemail.
        $isVoicemail = $recording->communication?->status === 'voicemail';
        $label = $isVoicemail ? 'Voicemail' : 'Call Recording';
        $displayName = $label . ' – ' . ($recording->recorded_at ?? now())->format('d M Y H:i');

        // Create the Document record on the client.
        $client->documents()->create([
            'solicitor_id' => $client->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));
        }
    }
}
