<?php

declare(strict_types=1);

namespace App\Support\OpsUx;

use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;

final class ActiveRuns
{
    private const int TERMINAL_SUCCESS_GRACE_SECONDS = 30;

    public static function queryForTenantId(?int $tenantId): Builder
    {
        return OperationRun::query()
            ->where('managed_environment_id', $tenantId)
            ->active()
            ->orderByDesc('created_at');
    }

    public static function shellVisibleQueryForTenantId(?int $tenantId): Builder
    {
        return OperationRun::query()
            ->where('managed_environment_id', $tenantId)
            ->where(function (Builder $query): void {
                $query
                    ->where(function (Builder $activeQuery): void {
                        $activeQuery->active();
                    })
                    ->orWhere(function (Builder $successQuery): void {
                        $successQuery
                            ->where('status', OperationRunStatus::Completed->value)
                            ->where('outcome', OperationRunOutcome::Succeeded->value)
                            ->whereNotNull('completed_at')
                            ->where('completed_at', '>=', now()->subSeconds(self::TERMINAL_SUCCESS_GRACE_SECONDS));
                    })
                    ->orWhere(function (Builder $followUpQuery): void {
                        $followUpQuery->terminalFollowUp();
                    });
            })
            ->orderByRaw(
                'case when status in (?, ?) then 0 when status = ? and outcome in (?, ?, ?) then 1 when status = ? and outcome = ? then 2 else 3 end',
                [
                    OperationRunStatus::Queued->value,
                    OperationRunStatus::Running->value,
                    OperationRunStatus::Completed->value,
                    OperationRunOutcome::Blocked->value,
                    OperationRunOutcome::PartiallySucceeded->value,
                    OperationRunOutcome::Failed->value,
                    OperationRunStatus::Completed->value,
                    OperationRunOutcome::Succeeded->value,
                ],
            )
            ->orderByRaw('coalesce(completed_at, started_at, created_at) desc')
            ->orderByDesc('id');
    }

    /**
     * @return Collection<int, OperationRun>
     */
    public static function visibleForTenantId(?int $tenantId, int $limit = 3): Collection
    {
        if (! is_int($tenantId) || $tenantId <= 0) {
            return collect();
        }

        return self::queryForTenantId($tenantId)
            ->limit(max(1, $limit))
            ->get();
    }

    /**
     * @return Collection<int, OperationRun>
     */
    public static function shellVisibleForTenantId(?int $tenantId, int $limit = 3): Collection
    {
        if (! is_int($tenantId) || $tenantId <= 0) {
            return collect();
        }

        return self::shellVisibleQueryForTenantId($tenantId)
            ->limit(max(1, $limit))
            ->get();
    }

    public static function countForTenantId(?int $tenantId): int
    {
        if (! is_int($tenantId) || $tenantId <= 0) {
            return 0;
        }

        return self::queryForTenantId($tenantId)->count();
    }

    public static function shellVisibleCountForTenantId(?int $tenantId): int
    {
        if (! is_int($tenantId) || $tenantId <= 0) {
            return 0;
        }

        return self::shellVisibleQueryForTenantId($tenantId)->count();
    }

    public static function existForTenant(ManagedEnvironment $tenant): bool
    {
        return self::existForTenantId((int) $tenant->getKey());
    }

    public static function existForTenantId(?int $tenantId): bool
    {
        if (! is_int($tenantId) || $tenantId <= 0) {
            return false;
        }

        return OperationRun::query()
            ->where('managed_environment_id', $tenantId)
            ->healthyActive()
            ->exists();
    }

    public static function pollingIntervalForTenant(?ManagedEnvironment $tenant): ?string
    {
        return $tenant instanceof ManagedEnvironment
            ? self::pollingIntervalForTenantId((int) $tenant->getKey())
            : null;
    }

    public static function pollingIntervalForTenantId(?int $tenantId): ?string
    {
        return self::existForTenantId($tenantId) ? '10s' : null;
    }
}
