<?php

namespace App\Listeners;

use App\Models\OperationRun;
use App\Models\RestoreRun;
use App\Services\OperationRunService;
use App\Support\RestoreRunStatus;

class SyncRestoreRunToOperationRun
{
    public function __construct(
        public OperationRunService $service
    ) {}

    public function handle(RestoreRun $restoreRun): void
    {
        $status = RestoreRunStatus::fromString($restoreRun->status);

        if (! $status) {
            return;
        }

        // Adapter row becomes visible from "previewed" onward.
        if (! in_array($status, [
            RestoreRunStatus::Previewed,
            RestoreRunStatus::Pending,
            RestoreRunStatus::Queued,
            RestoreRunStatus::Running,
            RestoreRunStatus::Completed,
            RestoreRunStatus::Partial,
            RestoreRunStatus::Failed,
            RestoreRunStatus::Cancelled,
            RestoreRunStatus::Aborted,
            RestoreRunStatus::CompletedWithErrors,
        ], true)) {
            return;
        }

        $inputs = [
            'restore_run_id' => (int) $restoreRun->getKey(),
            'backup_set_id' => (int) $restoreRun->backup_set_id,
            'is_dry_run' => (bool) ($restoreRun->is_dry_run ?? false),
        ];

        $opRun = null;

        if ($restoreRun->operation_run_id) {
            $opRun = OperationRun::query()->whereKey($restoreRun->operation_run_id)->first();

            if ($opRun?->type !== 'restore.execute') {
                $opRun = null;
            }
        }

        if (! $opRun) {
            $opRun = $this->service->ensureRun(
                tenant: $restoreRun->tenant,
                type: 'restore.execute',
                inputs: $inputs,
                initiator: null
            );
        }

        if ((int) ($restoreRun->operation_run_id ?? 0) !== (int) $opRun->getKey()) {
            RestoreRun::withoutEvents(function () use ($restoreRun, $opRun): void {
                $restoreRun->forceFill(['operation_run_id' => $opRun->getKey()])->save();
            });
        }

        [$opStatus, $opOutcome, $failures] = $this->mapStatus($status);

        $summaryCounts = [];
        $metadata = is_array($restoreRun->metadata) ? $restoreRun->metadata : [];

        foreach (['total', 'succeeded', 'failed', 'skipped'] as $key) {
            if (array_key_exists($key, $metadata) && is_numeric($metadata[$key])) {
                $summaryCounts[$key] = (int) $metadata[$key];
            }
        }

        $this->service->updateRun(
            $opRun,
            status: $opStatus,
            outcome: $opOutcome,
            summaryCounts: $summaryCounts,
            failures: $failures
        );
    }

    /**
     * @return array{0: string, 1: string, 2: array<int, array{code: string, message: string}>}
     */
    protected function mapStatus(RestoreRunStatus $status): array
    {
        return match ($status) {
            RestoreRunStatus::Previewed => ['completed', 'succeeded', []],
            RestoreRunStatus::Pending => ['queued', 'pending', []],
            RestoreRunStatus::Queued => ['queued', 'pending', []],
            RestoreRunStatus::Running => ['running', 'pending', []],
            RestoreRunStatus::Completed => ['completed', 'succeeded', []],
            RestoreRunStatus::Partial, RestoreRunStatus::CompletedWithErrors => ['completed', 'partially_succeeded', []],
            RestoreRunStatus::Failed, RestoreRunStatus::Aborted => ['completed', 'failed', []],
            RestoreRunStatus::Cancelled => ['completed', 'failed', [
                ['code' => 'restore.cancelled', 'message' => 'Restore run was cancelled.'],
            ]],
            default => ['running', 'pending', []],
        };
    }
}
