<?php

namespace App\Jobs\Synapse;

use App\Models\Communication\Call;
use App\Models\Communication\Recording;
use App\Models\Synapse\DataProtectionAudit;
use App\Models\Synapse\SynapseTranscript;
use App\Models\User\User;
use App\Services\HistoryService;
use App\Services\TaskService;
use App\Synapse\Agents\DataProtectionCheckAgent;
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 AuditDataProtectionCheckJob 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 $recordingId,
        public readonly string $classification,
    ) {}

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

        if (! $recording || ! $this->isEligible($recording)) {
            return;
        }

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

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

            return;
        }

        $audit = DataProtectionAudit::updateOrCreate(
            ['recording_id' => $recording->id],
            [
                'solicitor_id' => $recording->solicitor_id,
                'communication_id' => $recording->communication_id,
                'call_id' => $recording->call_id,
                'status' => 'processing',
                'error' => null,
            ],
        );

        try {
            $response = (new DataProtectionCheckAgent)->prompt(
                "CALL TRANSCRIPT:\n\n".$transcript->formatted_transcript,
            );
            $data = $response->structured;

            // Wrap-up can be saved while the AI request is in flight. Re-read the
            // call before persisting so a newly-selected Initial Call or voicemail
            // disposition remains authoritative.
            $recording->load(['communication', 'call']);

            if (! $this->isEligible($recording)) {
                $audit->update(['status' => 'excluded']);

                return;
            }

            $checks = collect($data['checks_confirmed'] ?? [])
                ->filter(fn (mixed $check) => in_array($check, ['Date of birth', 'Address', 'Postcode'], true))
                ->unique()
                ->values()
                ->all();
            $completedAtOutset = (bool) ($data['completed_at_outset'] ?? false);
            $result = count($checks) >= 2 && $completedAtOutset ? 'PASS' : 'FAIL';

            $audit->update([
                'status' => 'completed',
                'result' => $result,
                'number_of_checks_confirmed' => count($checks),
                'checks_confirmed' => $checks,
                'completed_at_outset' => $completedAtOutset,
                'evidence' => array_values($data['evidence'] ?? []),
                'reasoning' => $data['reasoning'] ?? null,
                'raw_output' => $response->toJson(),
                'error' => null,
            ]);

            if ($result === 'FAIL' && ! $audit->task_id) {
                $this->createReviewTask($audit, $recording, $tasks, $history);
            }
        } catch (\Throwable $e) {
            $audit->update([
                'status' => 'failed',
                'error' => $e->getMessage(),
            ]);

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

            throw $e;
        }
    }

    private function isEligible(Recording $recording): bool
    {
        if ($this->classification !== 'follow_up') {
            return false;
        }

        if (($recording->call?->call_type ?? 'client') !== 'client') {
            return false;
        }

        if ($recording->communication?->status === 'voicemail'
            || $recording->call?->disposition === 'left_voicemail'
            || $recording->call?->disposition === Call::DISPOSITION_INITIAL_CALL) {
            return false;
        }

        return $recording->communication?->direction === 'inbound'
            || $recording->call?->disposition === 'connected_successfully';
    }

    private function createReviewTask(
        DataProtectionAudit $audit,
        Recording $recording,
        TaskService $tasks,
        HistoryService $history,
    ): void {
        $reviewEmail = (string) config('synapse.data_protection_review_email');
        $assignee = User::query()
            ->where('email_address', $reviewEmail)
            ->first();
        $owner = $recording->communication?->communicable ?? $assignee;

        if (! $assignee instanceof User) {
            Log::error("AuditDataProtectionCheckJob: failed audit {$audit->id} could not be assigned because review user {$reviewEmail} was not found.");

            return;
        }

        $evidence = collect($audit->evidence)
            ->filter()
            ->map(fn (string $quote) => '“'.$quote.'”')
            ->implode(' ');
        $description = trim(
            ($audit->reasoning ?: 'The required data-protection checks were not completed at the outset of the call.')
            .($evidence ? ' Evidence: '.$evidence : ''),
        );

        $task = $tasks->addSystemTask(
            $history,
            $owner,
            $assignee,
            'Review failed data-protection call audit',
            now(),
            $description,
            (int) $recording->solicitor_id,
        );

        $audit->update(['task_id' => $task->id]);
    }
}
