<?php

namespace App\Jobs;

use App\Models\BackupSchedule;
use App\Models\BackupSet;
use App\Models\OperationRun;
use App\Services\Intune\AuditLogger;
use App\Services\OperationRunService;
use App\Services\Settings\SettingsResolver;
use App\Support\OperationCatalog;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

class ApplyBackupScheduleRetentionJob implements ShouldQueue
{
    use Queueable;

    public function __construct(public int $backupScheduleId) {}

    public function handle(AuditLogger $auditLogger, SettingsResolver $settingsResolver, OperationRunService $operationRunService): void
    {
        $schedule = BackupSchedule::query()
            ->with(['tenant.workspace'])
            ->find($this->backupScheduleId);

        if (! $schedule || ! $schedule->tenant) {
            return;
        }

        $operationRun = OperationRun::query()->create([
            'workspace_id' => (int) $schedule->tenant->workspace_id,
            'managed_environment_id' => (int) $schedule->managed_environment_id,
            'user_id' => null,
            'initiator_name' => 'System',
            'type' => OperationRunType::BackupScheduleRetention->value,
            'status' => OperationRunStatus::Running->value,
            'outcome' => OperationRunOutcome::Pending->value,
            'run_identity_hash' => hash('sha256', (string) $schedule->managed_environment_id.':'.OperationRunType::BackupScheduleRetention->value.':'.$schedule->id.':'.Str::uuid()->toString()),
            'context' => [
                'backup_schedule_id' => (int) $schedule->id,
            ],
            'started_at' => now(),
        ]);

        $keepLast = $schedule->retention_keep_last;
        $retentionFloor = 1;

        if ($schedule->tenant->workspace instanceof \App\Models\Workspace) {
            $resolvedFloor = $settingsResolver->resolveValue(
                workspace: $schedule->tenant->workspace,
                domain: 'backup',
                key: 'retention_min_floor',
                tenant: $schedule->tenant,
            );

            if (is_numeric($resolvedFloor)) {
                $retentionFloor = max(1, (int) $resolvedFloor);
            }
        }

        if ($keepLast === null && $schedule->tenant->workspace instanceof \App\Models\Workspace) {
            $resolvedDefault = $settingsResolver->resolveValue(
                workspace: $schedule->tenant->workspace,
                domain: 'backup',
                key: 'retention_keep_last_default',
                tenant: $schedule->tenant,
            );

            if (is_numeric($resolvedDefault)) {
                $keepLast = (int) $resolvedDefault;
            }
        }

        if (! is_numeric($keepLast)) {
            $keepLast = 30;
        }

        $keepLast = (int) $keepLast;

        if ($keepLast < $retentionFloor) {
            $keepLast = $retentionFloor;
        }

        /** @var Collection<int, int> $keepBackupSetIds */
        $keepBackupSetIds = OperationRun::query()
            ->where('managed_environment_id', (int) $schedule->managed_environment_id)
            ->whereIn('type', OperationCatalog::rawValuesForCanonical(OperationRunType::BackupScheduleExecute->value))
            ->where('status', OperationRunStatus::Completed->value)
            ->where('context->backup_schedule_id', (int) $schedule->id)
            ->whereNotNull('context->backup_set_id')
            ->orderByDesc('completed_at')
            ->orderByDesc('id')
            ->limit($keepLast)
            ->get()
            ->map(fn (OperationRun $run): ?int => is_numeric(data_get($run->context, 'backup_set_id')) ? (int) data_get($run->context, 'backup_set_id') : null)
            ->filter(fn (?int $id): bool => is_int($id) && $id > 0)
            ->values();

        /** @var Collection<int, int> $allBackupSetIds */
        $allBackupSetIds = OperationRun::query()
            ->where('managed_environment_id', (int) $schedule->managed_environment_id)
            ->whereIn('type', OperationCatalog::rawValuesForCanonical(OperationRunType::BackupScheduleExecute->value))
            ->where('status', OperationRunStatus::Completed->value)
            ->where('context->backup_schedule_id', (int) $schedule->id)
            ->whereNotNull('context->backup_set_id')
            ->get()
            ->map(fn (OperationRun $run): ?int => is_numeric(data_get($run->context, 'backup_set_id')) ? (int) data_get($run->context, 'backup_set_id') : null)
            ->filter(fn (?int $id): bool => is_int($id) && $id > 0)
            ->unique()
            ->values();

        /** @var Collection<int, int> $deleteBackupSetIds */
        $deleteBackupSetIds = $allBackupSetIds
            ->reject(fn (int $backupSetId): bool => $keepBackupSetIds->contains($backupSetId))
            ->values();

        $deletedCount = 0;

        if ($deleteBackupSetIds->isNotEmpty()) {
            BackupSet::query()
                ->where('managed_environment_id', $schedule->managed_environment_id)
                ->whereIn('id', $deleteBackupSetIds->all())
                ->whereNull('deleted_at')
                ->chunkById(200, function (Collection $sets) use (&$deletedCount): void {
                    foreach ($sets as $set) {
                        $set->delete();
                        $deletedCount++;
                    }
                });
        }

        $operationRunService->updateRun(
            $operationRun,
            status: OperationRunStatus::Completed->value,
            outcome: OperationRunOutcome::Succeeded->value,
            summaryCounts: [
                'total' => (int) $deleteBackupSetIds->count(),
                'processed' => (int) $deleteBackupSetIds->count(),
                'succeeded' => $deletedCount,
                'failed' => max(0, (int) $deleteBackupSetIds->count() - $deletedCount),
                'updated' => $deletedCount,
            ],
        );

        $auditLogger->log(
            tenant: $schedule->tenant,
            action: 'backup_schedule.retention_applied',
            resourceType: 'backup_schedule',
            resourceId: (string) $schedule->id,
            status: 'success',
            context: [
                'metadata' => [
                    'keep_last' => $keepLast,
                    'retention_floor' => $retentionFloor,
                    'deleted_backup_sets' => $deletedCount,
                    'operation_run_id' => (int) $operationRun->getKey(),
                ],
            ],
        );
    }
}
