<?php

namespace App\Jobs\Synapse;

use App\Models\Contact\Hub\ContactMessage;
use App\Models\Dsar\Dsar;
use App\Synapse\Agents\ClassifyDsarResponseAgent;
use App\Synapse\Agents\DsarMatcherAgent;
use App\Synapse\Agents\ExtractDsarReferenceAgent;
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 FindRelatedDsarJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

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

    public function __construct(
        public readonly int $messageId,
    ) {}

    public function handle(): void
    {
        $message = ContactMessage::find($this->messageId);

        if (!$message) {
            Log::warning("FindRelatedDsarJob: message {$this->messageId} not found.");
            return;
        }

        try {
            $prompt = $message->aiPrompt();
            $dsar   = null;

            // ── Stage 1: match to a DSAR (best-effort) ───────────────────────
            $match      = (new DsarMatcherAgent)->prompt($prompt);
            $dsarId     = $match['dsar_id'] ?? null;
            $confidence = $match['confidence'] ?? 'low';

            Log::info("FindRelatedDsarJob: message {$this->messageId} — dsar_id={$dsarId}, confidence={$confidence}.");

            if ($dsarId && in_array($confidence, ['high', 'medium'], true)) {
                $dsar = Dsar::withoutGlobalScopes()->find($dsarId);

                if ($dsar) {
                    $message->forceFill([
                        'messageable_type' => Dsar::class,
                        'messageable_id'   => $dsar->id,
                    ])->save();

                    Log::info("FindRelatedDsarJob: message {$this->messageId} linked to DSAR {$dsar->id}.");
                }
            }

            // ── Stage 2: classify what the institution said (always) ─────────
            $classification = (new ClassifyDsarResponseAgent)->prompt($prompt);
            $responseType   = $classification['response_type'] ?? 'other';

            Log::info("FindRelatedDsarJob: message {$this->messageId} response_type={$responseType}.");

            // ── Stage 3: extract institution reference (always) ───────────────
            $this->extractAndSaveReference($prompt, $dsar);

            // ── Stage 4: route to triage queue (always) ───────────────────────
            $queue = $this->resolveQueue($responseType);

            $message->forceFill([
                'triage_queue'  => $queue,
                'triage_status' => 'pending',
            ])->save();

            Log::info("FindRelatedDsarJob: message {$this->messageId} → triage_queue={$queue} (response_type={$responseType}, dsar_id=" . ($dsar?->id ?? 'none') . ").");

        } catch (\Throwable $e) {
            Log::error("FindRelatedDsarJob: failed for message {$this->messageId}.", [
                'error' => $e->getMessage(),
            ]);

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

    // ── Helpers ──────────────────────────────────────────────────────────────

    /**
     * Map response_type → triage_queue.
     *
     * dsar_evidence    — actual records received (data_provided, partial_data)
     * dsar_updates     — administrative responses needing no immediate action (acknowledgement, delay_notice)
     * dsar_needs_research — problematic responses requiring follow-up
     */
    private function resolveQueue(string $responseType): string
    {
        return match ($responseType) {
            'data_provided', 'partial_data'                                           => 'dsar_evidence',
            'acknowledgement', 'delay_notice'                                         => 'dsar_updates',
            'no_record', 'wrong_recipient', 'not_responsible', 'rejection', 'other'   => 'dsar_needs_research',
            default                                                                    => 'dsar_needs_research',
        };
    }

    /**
     * Ask the reference extractor and save to dsar->receiver_reference if
     * we found something with at least medium confidence and the field is empty.
     */
    private function extractAndSaveReference(string $prompt, ?Dsar $dsar): void
    {
        try {
            $result    = (new ExtractDsarReferenceAgent)->prompt($prompt);
            $reference = $result['reference'] ?? null;
            $refConf   = $result['confidence'] ?? 'low';

            if (!$reference || !in_array($refConf, ['high', 'medium'], true)) {
                return;
            }

            if ($dsar) {
                // Only write if the field is currently blank — don't overwrite manual entries.
                Dsar::withoutGlobalScopes()
                    ->where('id', $dsar->id)
                    ->whereNull('receiver_reference')
                    ->update(['receiver_reference' => $reference]);

                Log::info("FindRelatedDsarJob: extracted reference '{$reference}' (confidence={$refConf}) for DSAR {$dsar->id}.");
            } else {
                Log::info("FindRelatedDsarJob: message {$this->messageId} — extracted reference '{$reference}' (confidence={$refConf}), no DSAR matched.");
            }
        } catch (\Throwable $e) {
            // Reference extraction is best-effort — don't fail the whole job.
            Log::warning("FindRelatedDsarJob: reference extraction failed for message {$this->messageId}: {$e->getMessage()}");
        }
    }
}
