<?php

declare(strict_types=1);

namespace App\Jobs;

use App\Models\EvidenceSnapshot;
use App\Models\OperationRun;
use App\Services\Evidence\EvidenceSnapshotService;
use App\Services\OperationRunService;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;

class GenerateEvidenceSnapshotJob implements ShouldQueue
{
    use Queueable;

    public int $timeout = 240;

    public bool $failOnTimeout = true;

    public function __construct(
        public int $snapshotId,
        public int $operationRunId,
    ) {}

    public function handle(EvidenceSnapshotService $service, OperationRunService $operationRuns): void
    {
        $snapshot = EvidenceSnapshot::query()->with('tenant')->find($this->snapshotId);
        $operationRun = OperationRun::query()->find($this->operationRunId);

        if (! $snapshot instanceof EvidenceSnapshot || ! $operationRun instanceof OperationRun || ! $snapshot->tenant) {
            return;
        }

        $operationRuns->updateRun($operationRun, OperationRunStatus::Running->value, OperationRunOutcome::Pending->value);
        $snapshot->update(['status' => EvidenceSnapshotStatus::Generating->value]);

        try {
            $payload = $service->buildSnapshotPayload($snapshot->tenant);
            $itemCount = count($payload['items']);

            $operationRun = $operationRuns->updateRun(
                $operationRun,
                status: OperationRunStatus::Running->value,
                outcome: OperationRunOutcome::Pending->value,
                summaryCounts: [
                    'total' => $itemCount,
                    'processed' => 0,
                    'succeeded' => 0,
                    'failed' => 0,
                    'skipped' => 0,
                ],
            );

            $previousActive = EvidenceSnapshot::query()
                ->where('managed_environment_id', (int) $snapshot->managed_environment_id)
                ->where('workspace_id', (int) $snapshot->workspace_id)
                ->where('status', EvidenceSnapshotStatus::Active->value)
                ->whereKeyNot((int) $snapshot->getKey())
                ->first();

            $snapshot->items()->delete();

            foreach ($payload['items'] as $item) {
                $snapshot->items()->create([
                    'managed_environment_id' => (int) $snapshot->managed_environment_id,
                    'workspace_id' => (int) $snapshot->workspace_id,
                    'dimension_key' => $item['dimension_key'],
                    'state' => $item['state'],
                    'required' => $item['required'],
                    'source_kind' => $item['source_kind'],
                    'source_record_type' => $item['source_record_type'],
                    'source_record_id' => $item['source_record_id'],
                    'source_fingerprint' => $item['source_fingerprint'],
                    'measured_at' => $item['measured_at'],
                    'freshness_at' => $item['freshness_at'],
                    'summary_payload' => $item['summary_payload'],
                    'sort_order' => $item['sort_order'],
                ]);

                $operationRun = $operationRuns->incrementSummaryCounts($operationRun, [
                    'processed' => 1,
                    'created' => 1,
                ]);
            }

            if ($previousActive instanceof EvidenceSnapshot && $previousActive->fingerprint !== $payload['fingerprint']) {
                $previousActive->update([
                    'status' => EvidenceSnapshotStatus::Superseded->value,
                ]);
            }

            $snapshot->update([
                'fingerprint' => $payload['fingerprint'],
                'previous_fingerprint' => $previousActive?->fingerprint,
                'status' => EvidenceSnapshotStatus::Active->value,
                'completeness_state' => $payload['completeness'],
                'generated_at' => now(),
                'summary' => $payload['summary'],
            ]);

            $operationRuns->updateRun(
                $operationRun,
                status: OperationRunStatus::Completed->value,
                outcome: OperationRunOutcome::Succeeded->value,
                summaryCounts: [
                    'total' => $itemCount,
                    'processed' => $itemCount,
                    'created' => $itemCount,
                    'finding_count' => (int) ($payload['summary']['finding_count'] ?? 0),
                    'report_count' => (int) ($payload['summary']['report_count'] ?? 0),
                    'operation_count' => (int) ($payload['summary']['operation_count'] ?? 0),
                    'errors_recorded' => 0,
                ],
            );
        } catch (Throwable $throwable) {
            $snapshot->update([
                'status' => EvidenceSnapshotStatus::Failed->value,
                'summary' => [
                    'error' => $throwable->getMessage(),
                ],
            ]);

            $operationRuns->updateRun(
                $operationRun,
                status: OperationRunStatus::Completed->value,
                outcome: OperationRunOutcome::Failed->value,
                failures: [
                    [
                        'code' => 'evidence_snapshot_generation.failed',
                        'message' => $throwable->getMessage(),
                    ],
                ],
            );

            throw $throwable;
        }
    }
}
