<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Domain\Account\Enums\AccountStatus;
use App\Domain\Account\Models\Account;
use App\Domain\Resource\Services\CgroupManager;
use Illuminate\Console\Command;
use Throwable;

/**
 * Bulk re-apply cgroup limits — tüm aktif hesaplara paket+override limit'lerini yeniden yazar.
 *
 * Kullanım senaryoları:
 *  - Paket tanımları toplu güncellendiğinde (örn. tüm "Starter" paketleri 512→768 MB)
 *  - Yeni sunucu kurulumu sonrası mevcut hesaplara cgroup yazımı yapmak
 *  - cgroup config drift düzeltmek (systemd slice'lar elle değiştirilmiş)
 *
 * Drift detection: --check flag'ı sadece hangi hesapların drift olduğunu
 * raporlar, apply yapmaz. --apply ile gerçek uygulama.
 *
 *   php artisan onoxsoft:cgroups:bulk-apply              # dry-run (sadece rapor)
 *   php artisan onoxsoft:cgroups:bulk-apply --apply      # gerçek apply
 *   php artisan onoxsoft:cgroups:bulk-apply --account=onx_demo01
 *   php artisan onoxsoft:cgroups:bulk-apply --suspended  # suspended hesaplara da uygula
 */
class CgroupsBulkApplyCommand extends Command
{
    protected $signature = 'onoxsoft:cgroups:bulk-apply
        {--apply : Gerçek uygulama yap (varsayılan: dry-run rapor)}
        {--account= : Sadece bu username için uygula}
        {--suspended : Suspended hesapları da kapsa (varsayılan: yalnız active)}
        {--continue-on-error : Bir hesap başarısız olursa durmak yerine devam et (varsayılan davranış)}';

    protected $description = 'Cgroup limit\'lerini toplu yeniden uygula — paket güncellemesi veya drift düzeltme';

    public function handle(CgroupManager $cgroups): int
    {
        $query = Account::query();

        if (! $this->option('suspended')) {
            $query->where('status', AccountStatus::Active);
        }

        $username = $this->option('account');
        if (is_string($username) && $username !== '') {
            $query->where('username', $username);
        }

        $accounts = $query->with('package')->get();

        if ($accounts->isEmpty()) {
            $this->warn('Eşleşen hesap yok.');
            return self::SUCCESS;
        }

        $apply = (bool) $this->option('apply');
        $mode = $apply ? 'APPLY' : 'DRY-RUN';

        $this->info("[{$mode}] {$accounts->count()} hesaba cgroup uygulanacak.");
        $this->newLine();

        $applied = 0;
        $failed = 0;
        $start = microtime(true);

        foreach ($accounts as $account) {
            $limits = $cgroups->effectiveLimitsFor($account);
            $limitSummary = sprintf(
                'CPU=%d%% RAM=%dMB IO=R%d/W%dMBps proc=%d',
                $limits['cpu_limit_percent'],
                $limits['memory_limit_mb'],
                $limits['io_read_mbps'],
                $limits['io_write_mbps'],
                $limits['entry_processes_limit'],
            );

            if (! $apply) {
                $this->line("• {$account->username} → {$limitSummary}");
                continue;
            }

            try {
                $cgroups->applyTo($account);
                $this->line("✓ {$account->username} → {$limitSummary}");
                $applied++;
            } catch (Throwable $e) {
                $this->error("✗ {$account->username}: {$e->getMessage()}");
                $failed++;
            }
        }

        $dur = round(microtime(true) - $start, 1);
        $this->newLine();

        if ($apply) {
            $this->info("Tamamlandı — {$applied} hesap uygulandı, {$failed} başarısız ({$dur}s).");
            return $failed > 0 ? self::FAILURE : self::SUCCESS;
        }

        $this->warn("DRY-RUN — gerçek apply için --apply flag'ı ile tekrar çalıştırın.");
        return self::SUCCESS;
    }
}
