<?php

namespace App\Jobs\Synapse;

use App\Models\Communication\Recording;
use App\Models\Synapse\SynapseTranscript;
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\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;

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

    public int $tries   = 3;
    public int $timeout = 120;
    public int $backoff = 30;

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

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

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

        if (!$recording->hasStoredFile()) {
            Log::warning("AutoTranscribeRecordingJob: recording {$this->recordingId} has no stored file.");
            return;
        }

        SynapseTranscript::updateOrCreate(
            ['recording_id' => $recording->id],
            [
                'solicitor_id'           => $recording->solicitor_id,
                'provider'               => 'assemblyai',
                'provider_transcript_id' => null,
                'status'                 => 'processing',
                'raw_transcript'         => '',
                'formatted_transcript'   => '',
                'error'                  => null,
            ]
        );

        try {
            $audio    = Storage::disk($recording->storage_disk)->get($recording->storage_path);
            $apiKey   = config('synapse.assemblyai_key');
            $baseUrl  = 'https://api.assemblyai.com';

            // Step 1: Upload the audio file.
            $uploadResponse = Http::withHeaders(['Authorization' => $apiKey])
                ->withBody($audio, 'application/octet-stream')
                ->timeout(120)
                ->throw()
                ->post("{$baseUrl}/v2/upload");

            $audioUrl = $uploadResponse->json('upload_url');

            if (!$audioUrl) {
                throw new \RuntimeException('AssemblyAI upload did not return an upload_url.');
            }

            // Step 2: Submit the transcription request.
            $payload = [
                'audio_url'          => $audioUrl,
                'speaker_labels'     => true,
                'speech_models'      => [config('synapse.assemblyai_model', 'universal-3-pro')],
                'language_code'      => config('synapse.assemblyai_language', 'en_uk'),
                'prompt'             => config('synapse.transcription_prompt'),
                'sentiment_analysis' => true,
            ];

            $speakerNames = $this->resolveSpeakerNames($recording);
            if (!empty($speakerNames)) {
                $payload['keyterms_prompt'] = $speakerNames;
            }

            $transcribeResponse = Http::withHeaders(['Authorization' => $apiKey])
                ->timeout(30)
                ->throw()
                ->post("{$baseUrl}/v2/transcript", $payload);

            $transcriptId = $transcribeResponse->json('id');

            if (!$transcriptId) {
                throw new \RuntimeException('AssemblyAI did not return a transcript ID.');
            }

            SynapseTranscript::where('recording_id', $recording->id)
                ->update(['provider_transcript_id' => $transcriptId]);

            Log::info("AutoTranscribeRecordingJob: submitted recording {$this->recordingId} → AssemblyAI {$transcriptId}");

            // Step 3: Hand off to the polling job (first check after 60s).
            CheckTranscriptionStatusJob::dispatch($this->recordingId, $transcriptId)
                ->delay(now()->addSeconds(60));

        } catch (\Throwable $e) {
            SynapseTranscript::where('recording_id', $recording->id)
                ->update(['status' => 'failed', 'error' => $e->getMessage()]);

            Log::error("AutoTranscribeRecordingJob: failed for recording {$this->recordingId}.", [
                'error' => $e->getMessage(),
            ]);

            report($e);
            $this->fail($e);
        }
    }

    protected function resolveSpeakerNames(Recording $recording): array
    {
        $names = [];

        $agentName = $recording->call?->answeredByAgent?->name
                  ?? $recording->call?->assignedAgent?->name;

        if (filled($agentName)) {
            $names[] = $agentName;
        }

        $communicable = $recording->communication?->communicable;
        if ($communicable) {
            foreach (['full_name', 'name', 'display_name'] as $attr) {
                if (filled($communicable->{$attr} ?? null)) {
                    $names[] = (string) $communicable->{$attr};
                    break;
                }
            }
        }

        return $names;
    }
}
