<?php

namespace App\Console\Commands;

use App\Services\AdapterRunReconciler;
use Illuminate\Console\Command;
use Throwable;

class OpsReconcileAdapterRuns extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'ops:reconcile-adapter-runs
                            {--type= : Adapter run type (e.g. restore.execute)}
                            {--tenant= : ManagedEnvironment ID}
                            {--older-than=60 : Only consider runs older than N minutes}
                            {--dry-run=true : Preview only (true/false)}
                            {--limit=50 : Max number of runs to inspect}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Reconcile stale adapter-backed operation runs from DB-only source-of-truth records.';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        try {
            /** @var AdapterRunReconciler $reconciler */
            $reconciler = app(AdapterRunReconciler::class);

            $type = $this->option('type');
            $type = is_string($type) && trim($type) !== '' ? trim($type) : null;

            $tenantId = $this->option('tenant');
            $tenantId = is_numeric($tenantId) ? (int) $tenantId : null;

            $olderThanMinutes = $this->option('older-than');
            $olderThanMinutes = is_numeric($olderThanMinutes) ? (int) $olderThanMinutes : 60;
            $olderThanMinutes = max(1, $olderThanMinutes);

            $limit = $this->option('limit');
            $limit = is_numeric($limit) ? (int) $limit : 50;
            $limit = max(1, $limit);

            $dryRun = $this->option('dry-run');
            $dryRun = filter_var($dryRun, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE);
            $dryRun = $dryRun ?? true;

            $result = $reconciler->reconcile([
                'type' => $type,
                'managed_environment_id' => $tenantId,
                'older_than_minutes' => $olderThanMinutes,
                'limit' => $limit,
                'dry_run' => $dryRun,
            ]);

            $changes = $result['changes'] ?? [];

            usort($changes, static fn (array $a, array $b): int => ((int) ($a['operation_run_id'] ?? 0)) <=> ((int) ($b['operation_run_id'] ?? 0)));

            $this->info('Adapter run reconciliation');
            $this->line('dry_run: '.($dryRun ? 'true' : 'false'));
            $this->line('type: '.($type ?? '(all supported)'));
            $this->line('tenant: '.($tenantId ? (string) $tenantId : '(all)'));
            $this->line('older_than_minutes: '.$olderThanMinutes);
            $this->line('limit: '.$limit);
            $this->newLine();

            $this->line('candidates: '.(int) ($result['candidates'] ?? 0));
            $this->line('reconciled: '.(int) ($result['reconciled'] ?? 0));
            $this->line('skipped: '.(int) ($result['skipped'] ?? 0));
            $this->newLine();

            if ($changes === []) {
                $this->info('No changes.');

                return self::SUCCESS;
            }

            $rows = [];

            foreach ($changes as $change) {
                $before = is_array($change['before'] ?? null) ? $change['before'] : [];
                $after = is_array($change['after'] ?? null) ? $change['after'] : [];

                $rows[] = [
                    'applied' => ($change['applied'] ?? false) ? 'yes' : 'no',
                    'operation_run_id' => (int) ($change['operation_run_id'] ?? 0),
                    'type' => (string) ($change['type'] ?? ''),
                    'source_id' => (int) ($change['restore_run_id'] ?? 0),
                    'before' => (string) (($before['status'] ?? '').'/'.($before['outcome'] ?? '')),
                    'after' => (string) (($after['status'] ?? '').'/'.($after['outcome'] ?? '')),
                ];
            }

            $this->table(
                ['applied', 'operation_run_id', 'type', 'source_id', 'before', 'after'],
                $rows,
            );

            return self::SUCCESS;
        } catch (Throwable $e) {
            $this->error('Reconciliation failed: '.$e->getMessage());

            return self::FAILURE;
        }
    }
}
