<?php

namespace App\Jobs\Synapse;

use App\Models\Communication\Recording;
use App\Models\Synapse\CallScore;
use App\Models\Synapse\SynapseTranscript;
use App\Synapse\Agents\CallClassifierAgent;
use App\Synapse\Agents\CallScorerAgent;
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;

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

    public int $tries   = 2;
    public int $timeout = 300;  // classifier (~10s) + scorer (~120s) + headroom
    public int $backoff = 30;

    public function __construct(
        public readonly int $recordingId,
        public readonly bool $force = false,
    ) {}

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

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

        $transcript = SynapseTranscript::where('recording_id', $this->recordingId)
            ->where('status', 'completed')
            ->first();

        if (!$transcript || blank($transcript->formatted_transcript)) {
            Log::warning("ClassifyAndScoreCallJob: no completed transcript for recording {$this->recordingId}.");
            return;
        }

        // Skip calls that are too short to be meaningful (< 30 seconds or < 15 words).
        $wordCount = str_word_count(strip_tags($transcript->formatted_transcript));
        if (!$this->force && (($recording->duration !== null && $recording->duration < 30) || $wordCount < 15)) {
            Log::info("ClassifyAndScoreCallJob: skipping recording {$this->recordingId} — too short to classify (duration={$recording->duration}s, words={$wordCount}).");
            return;
        }

        $score = CallScore::updateOrCreate(
            ['recording_id' => $this->recordingId],
            [
                'solicitor_id'     => $recording->solicitor_id,
                'communication_id' => $recording->communication_id,
                'call_id'          => $recording->call_id,
                'status'           => 'processing',
                'error'            => null,
            ]
        );

        try {
            $classifierPrompt = "CALL TRANSCRIPT:\n\n" . $transcript->formatted_transcript;
            $classification   = (new CallClassifierAgent)->prompt($classifierPrompt)['classification'] ?? 'not_applicable';

            Log::debug("ClassifyAndScoreCallJob: classification={$classification} for recording {$this->recordingId}");

            if ($classification === 'not_applicable') {
                $score->update([
                    'is_initial_call' => null,
                    'status'          => 'completed',
                ]);
                return;
            }

            $isInitial = $classification === 'initial';

            if (!$isInitial) {
                $score->update([
                    'is_initial_call' => false,
                    'status'          => 'completed',
                ]);
                return;
            }

            $scorerPrompt = "CALL TRANSCRIPT:\n\n" . $transcript->formatted_transcript;
            $scorerResponse = (new CallScorerAgent)->prompt($scorerPrompt);
            $data           = $scorerResponse->structured;

            Log::debug("ClassifyAndScoreCallJob: scorer returned for recording {$this->recordingId}.", [
                'total_score' => $data['total_score'] ?? null,
                'risk_rating' => $data['risk_rating'] ?? null,
                'has_sections'=> isset($data['sections']),
            ]);

            $totalScore = (int) $data['total_score'];
            $riskRating = $this->resolveRiskRating($totalScore);

            $score->update([
                'is_initial_call'    => true,
                'status'             => 'completed',
                'total_score'        => $totalScore,
                'risk_rating'        => $riskRating,
                'passed'             => ($data['passed'] ?? false) && $riskRating !== 'high_risk',
                'auto_fail'          => (bool) ($data['auto_fail'] ?? false),
                'auto_fail_triggers' => $data['auto_fail_triggers'] ?? [],
                'section_scores'     => $data['sections'] ?? null,
                'summary'            => $data['summary'] ?? null,
                'raw_output'         => $scorerResponse->toJson(),
            ]);

        } catch (\Throwable $e) {
            $score->update([
                'status' => 'failed',
                'error'  => $e->getMessage(),
            ]);

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

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

    private function resolveRiskRating(int $score): string
    {
        return match (true) {
            $score >= 90 => 'low_risk',
            $score >= 80 => 'acceptable',
            $score >= 70 => 'compliance_concern',
            default      => 'high_risk',
        };
    }
}
