<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\ManagedEnvironment;
use App\Services\Operations\OperationLifecycleReconciler;
use App\Support\Operations\OperationLifecyclePolicy;
use Illuminate\Console\Command;

class TenantpilotReconcileOperationRuns extends Command
{
    protected $signature = 'tenantpilot:operation-runs:reconcile
        {--type=* : Limit reconciliation to one or more covered operation types}
        {--tenant=* : Limit reconciliation to managed_environment_id or tenant external_id}
        {--workspace=* : Limit reconciliation to workspace ids}
        {--limit=100 : Maximum number of active runs to inspect}
        {--dry-run : Report the changes without writing them}';

    protected $description = 'Reconcile stale covered operation runs back to deterministic terminal truth.';

    public function handle(
        OperationLifecycleReconciler $reconciler,
        OperationLifecyclePolicy $policy,
    ): int {
        $types = array_values(array_filter(
            (array) $this->option('type'),
            static fn (mixed $type): bool => is_string($type) && trim($type) !== '',
        ));
        $workspaceIds = array_values(array_filter(
            array_map(
                static fn (mixed $workspaceId): int => is_numeric($workspaceId) ? (int) $workspaceId : 0,
                (array) $this->option('workspace'),
            ),
            static fn (int $workspaceId): bool => $workspaceId > 0,
        ));
        $tenantIds = $this->resolveTenantIds(array_values(array_filter((array) $this->option('tenant'))));
        $dryRun = (bool) $this->option('dry-run');

        if ($types === []) {
            $types = $policy->coveredTypeNames();
        }

        $result = $reconciler->reconcile([
            'types' => $types,
            'tenant_ids' => $tenantIds,
            'workspace_ids' => $workspaceIds,
            'limit' => max(1, (int) $this->option('limit')),
            'dry_run' => $dryRun,
        ]);

        $rows = collect($result['changes'] ?? [])
            ->map(static function (array $change): array {
                return [
                    'Run' => (string) ($change['operation_run_id'] ?? '—'),
                    'Type' => (string) ($change['type'] ?? '—'),
                    'Reason' => (string) ($change['reason_code'] ?? '—'),
                    'Applied' => (($change['applied'] ?? false) === true) ? 'yes' : 'no',
                ];
            })
            ->values()
            ->all();

        if ($rows !== []) {
            $this->table(['Run', 'Type', 'Reason', 'Applied'], $rows);
        }

        $this->info(sprintf(
            'Inspected %d run(s); reconciled %d; skipped %d.',
            (int) ($result['candidates'] ?? 0),
            (int) ($result['reconciled'] ?? 0),
            (int) ($result['skipped'] ?? 0),
        ));

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

        return self::SUCCESS;
    }

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

        $tenantIds = [];

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

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

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