<?php

namespace App\Console\Commands;

use App\Models\BackupSchedule;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Services\OperationRunService;
use App\Services\Operations\OperationLifecycleReconciler;
use App\Support\OperationCatalog;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunType;
use Illuminate\Console\Command;

class TenantpilotReconcileBackupScheduleOperationRuns extends Command
{
    protected $signature = 'tenantpilot:operation-runs:reconcile-backup-schedules
        {--tenant=* : Limit to managed_environment_id/external_id}
        {--older-than=5 : Only reconcile runs older than N minutes}
        {--dry-run : Do not write changes}';

    protected $description = 'Reconcile stuck backup schedule OperationRuns without legacy run-table lookups.';

    public function handle(
        OperationRunService $operationRunService,
        OperationLifecycleReconciler $operationLifecycleReconciler,
    ): int {
        $tenantIdentifiers = array_values(array_filter((array) $this->option('tenant')));
        $olderThanMinutes = max(0, (int) $this->option('older-than'));
        $dryRun = (bool) $this->option('dry-run');

        $query = OperationRun::query()
            ->whereIn('type', OperationCatalog::rawValuesForCanonical(OperationRunType::BackupScheduleExecute->value))
            ->whereIn('status', ['queued', 'running']);

        if ($olderThanMinutes > 0) {
            $query->where('created_at', '<', now()->subMinutes($olderThanMinutes));
        }

        if ($tenantIdentifiers !== []) {
            $tenantIds = $this->resolveTenantIds($tenantIdentifiers);

            if ($tenantIds === []) {
                $this->info('No tenants matched the provided identifiers.');

                return self::SUCCESS;
            }

            $query->whereIn('managed_environment_id', $tenantIds);
        }

        $reconciled = 0;
        $skipped = 0;
        $failed = 0;

        foreach ($query->cursor() as $operationRun) {
            $backupScheduleId = data_get($operationRun->context, 'backup_schedule_id');

            if (! is_numeric($backupScheduleId)) {
                if (! $dryRun) {
                    $operationRunService->updateRun(
                        $operationRun,
                        status: 'completed',
                        outcome: OperationRunOutcome::Failed->value,
                        failures: [
                            [
                                'code' => 'backup_schedule.missing_context',
                                'message' => 'Backup schedule context is missing from this operation run.',
                            ],
                        ],
                    );
                }

                $failed++;

                continue;
            }

            $schedule = BackupSchedule::query()
                ->whereKey((int) $backupScheduleId)
                ->where('managed_environment_id', (int) $operationRun->managed_environment_id)
                ->first();

            if (! $schedule instanceof BackupSchedule) {
                if (! $dryRun) {
                    $operationRunService->updateRun(
                        $operationRun,
                        status: 'completed',
                        outcome: OperationRunOutcome::Failed->value,
                        failures: [
                            [
                                'code' => 'backup_schedule.not_found',
                                'message' => 'Backup schedule not found for this operation run.',
                            ],
                        ],
                    );
                }

                $failed++;

                continue;
            }

            $change = $operationLifecycleReconciler->reconcileRun($operationRun, $dryRun);

            if ($change !== null) {
                $reconciled++;

                continue;
            }

            $skipped++;
        }

        $this->info(sprintf(
            'Reconciled %d run(s), skipped %d, failed %d.',
            $reconciled,
            $skipped,
            $failed,
        ));

        if ($dryRun) {
            $this->comment('Dry-run: no changes written.');
        }

        return self::SUCCESS;
    }

    /**
     * @param  array<int, string>  $tenantIdentifiers
     * @return array<int>
     */
    private function resolveTenantIds(array $tenantIdentifiers): array
    {
        $tenantIds = [];

        foreach ($tenantIdentifiers as $identifier) {
            $tenant = ManagedEnvironment::query()
                ->forTenant($identifier)
                ->first();

            if ($tenant) {
                $tenantIds[] = (int) $tenant->getKey();
            }
        }

        return array_values(array_unique($tenantIds));
    }
}
