<?php

namespace App\Models;

use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
use App\Support\RestoreRunStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;

class RestoreRun extends Model
{
    use DerivesWorkspaceIdFromTenant;
    use HasFactory;
    use SoftDeletes;

    protected $guarded = [];

    protected $casts = [
        'is_dry_run' => 'boolean',
        'idempotency_key' => 'string',
        'requested_items' => 'array',
        'preview' => 'array',
        'results' => 'array',
        'metadata' => 'array',
        'group_mapping' => 'array',
        'started_at' => 'datetime',
        'completed_at' => 'datetime',
    ];

    public function tenant(): BelongsTo
    {
        return $this->belongsTo(ManagedEnvironment::class, 'managed_environment_id');
    }

    public function backupSet(): BelongsTo
    {
        return $this->belongsTo(BackupSet::class)->withTrashed();
    }

    public function operationRun(): BelongsTo
    {
        return $this->belongsTo(OperationRun::class);
    }

    public function scopeDeletable(Builder $query): Builder
    {
        return $query->whereIn('status', array_map(
            static fn (RestoreRunStatus $status): string => $status->value,
            [
                RestoreRunStatus::Draft,
                RestoreRunStatus::Scoped,
                RestoreRunStatus::Checked,
                RestoreRunStatus::Previewed,
                RestoreRunStatus::Completed,
                RestoreRunStatus::Partial,
                RestoreRunStatus::Failed,
                RestoreRunStatus::Cancelled,
                RestoreRunStatus::Aborted,
                RestoreRunStatus::CompletedWithErrors,
            ]
        ));
    }

    public function isDeletable(): bool
    {
        $status = RestoreRunStatus::fromString($this->status);

        return $status?->isDeletable() ?? false;
    }

    // Group mapping helpers
    public function hasGroupMapping(): bool
    {
        return ! empty($this->group_mapping);
    }

    public function getMappedGroupId(string $sourceGroupId): ?string
    {
        $mapping = $this->group_mapping ?? [];

        return $mapping[$sourceGroupId] ?? null;
    }

    public function isGroupSkipped(string $sourceGroupId): bool
    {
        $mapping = $this->group_mapping ?? [];

        return ($mapping[$sourceGroupId] ?? null) === 'SKIP';
    }

    public function getUnmappedGroupIds(array $sourceGroupIds): array
    {
        return array_diff($sourceGroupIds, array_keys($this->group_mapping ?? []));
    }

    public function addGroupMapping(string $sourceGroupId, string $targetGroupId): void
    {
        $mapping = $this->group_mapping ?? [];
        $mapping[$sourceGroupId] = $targetGroupId;
        $this->group_mapping = $mapping;
    }

    // Assignment restore outcome helpers
    public function getAssignmentRestoreOutcomes(): array
    {
        $results = $this->results ?? [];

        if (isset($results['assignment_outcomes']) && is_array($results['assignment_outcomes'])) {
            return $results['assignment_outcomes'];
        }

        if (isset($results['items']) && is_array($results['items'])) {
            return collect($results['items'])
                ->pluck('assignment_outcomes')
                ->flatten(1)
                ->filter()
                ->values()
                ->all();
        }

        if (! is_array($results)) {
            return [];
        }

        return collect($results)
            ->pluck('assignment_outcomes')
            ->flatten(1)
            ->filter(static fn (mixed $outcome): bool => is_array($outcome))
            ->values()
            ->all();
    }

    public function getSuccessfulAssignmentsCount(): int
    {
        return count(array_filter(
            $this->getAssignmentRestoreOutcomes(),
            static fn (mixed $outcome): bool => is_array($outcome) && ($outcome['status'] ?? null) === 'success'
        ));
    }

    public function getFailedAssignmentsCount(): int
    {
        return count(array_filter(
            $this->getAssignmentRestoreOutcomes(),
            static fn (mixed $outcome): bool => is_array($outcome) && ($outcome['status'] ?? null) === 'failed'
        ));
    }

    public function getSkippedAssignmentsCount(): int
    {
        return count(array_filter(
            $this->getAssignmentRestoreOutcomes(),
            static fn (mixed $outcome): bool => is_array($outcome) && ($outcome['status'] ?? null) === 'skipped'
        ));
    }

    /**
     * @return array<string, mixed>
     */
    public function scopeBasis(): array
    {
        $metadata = is_array($this->metadata) ? $this->metadata : [];

        return is_array($metadata['scope_basis'] ?? null) ? $metadata['scope_basis'] : [];
    }

    /**
     * @return array<string, mixed>
     */
    public function checkBasis(): array
    {
        $metadata = is_array($this->metadata) ? $this->metadata : [];

        return is_array($metadata['check_basis'] ?? null) ? $metadata['check_basis'] : [];
    }

    /**
     * @return array<string, mixed>
     */
    public function previewBasis(): array
    {
        $metadata = is_array($this->metadata) ? $this->metadata : [];

        return is_array($metadata['preview_basis'] ?? null) ? $metadata['preview_basis'] : [];
    }

    /**
     * @return array<string, mixed>
     */
    public function executionSafetySnapshot(): array
    {
        $metadata = is_array($this->metadata) ? $this->metadata : [];

        return is_array($metadata['execution_safety_snapshot'] ?? null)
            ? $metadata['execution_safety_snapshot']
            : [];
    }
}
