<?php

declare(strict_types=1);

namespace App\Jobs;

use App\Domain\Migration\Services\CpanelRestoreEngine;
use App\Domain\Migration\Sources\SourcePanelRegistry;
use App\Domain\Sysapi\Contracts\SysapiBridge;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * WHM Transfer Tool — toplu pull job (Phase 2).
 *
 * Her seçilen cPanel hesabı için ayrı bir job dispatch edilir:
 *  1. sysapi cpanel-pull-remote → uzak sunucudan pkgacct + rsync
 *  2. CpanelRestoreEngine::dryRun → inspect_data DB'ye yaz, status=planned
 *  3. (otomatik execute kapalı — admin "Plan" sayfasından review edip execute eder)
 *
 * Password yalnız $authArgs içinde bellekte; SerializesModels kullansa da
 * Queueable payload'ı Redis'e gider — bu yüzden encryption_key veya kısa
 * süreli token-only auth tercih edilmeli. Şimdilik password queue payload'ında
 * tutulur (security trade-off — production'da SSH key zorunlu önerilir).
 *
 * Queue: backups (long-running, pkgacct 5-30 dk sürebilir).
 */
class BulkCpanelPullJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 3600;
    public int $tries = 1;
    public string $queue = 'backups';

    /**
     * @param  array<string, mixed>  $authArgs  SSH auth (host/port/login_user/auth_method + password|ssh_key_id)
     * @param  string  $sourcePanel  cpanel | directadmin | plesk
     */
    public function __construct(
        public readonly int $migrationJobId,
        public readonly array $authArgs,
        public readonly string $sourceUsername,
        public readonly string $sourcePanel = 'cpanel',
    ) {}

    public function handle(SysapiBridge $sysapi, CpanelRestoreEngine $engine): void
    {
        $job = DB::table('migration_jobs')->find($this->migrationJobId);
        if (! $job) {
            Log::warning('bulk_pull.job_missing', ['job_id' => $this->migrationJobId]);
            return;
        }

        $panelMeta = SourcePanelRegistry::get($this->sourcePanel);
        $pullAction = $panelMeta['sysapi']['pull_remote'];

        DB::table('migration_jobs')->where('id', $job->id)->update([
            'status' => 'inspecting',
            'current_step' => $this->sourcePanel.'_pull_running',
            'progress_pct' => 5,
            'started_at' => now(),
            'updated_at' => now(),
        ]);

        try {
            // 1. sysapi pull-remote — source_panel'e göre doğru action seçilir
            //    cpanel       → cpanel-pull-remote (pkgacct + rsync)
            //    directadmin  → directadmin-pull-remote (admin_backup_user.sh + rsync)
            //    plesk        → plesk-pull-remote (pleskbackup + rsync)
            $pullArgs = array_merge($this->authArgs, [
                'source_username' => $this->sourceUsername,
                'backup_script' => $panelMeta['backup_script'],
                'output_dir' => storage_path('app/migrations'),
            ]);
            $pullResult = $sysapi->run($pullAction, $pullArgs, 3600);

            if ($pullResult->failed()) {
                $this->markFailed($job->id, "{$panelMeta['short']} pull başarısız: ".$pullResult->stderr);
                return;
            }

            $localPath = (string) ($pullResult->data['local_path'] ?? '');
            $tarSize = (int) ($pullResult->data['tar_size_bytes'] ?? 0);

            if ($localPath === '') {
                $this->markFailed($job->id, 'Pull tamamlandı fakat local_path döndürülmedi.');
                return;
            }

            DB::table('migration_jobs')->where('id', $job->id)->update([
                'tar_filename' => $localPath,
                'tar_size_bytes' => $tarSize,
                'current_step' => 'inspecting',
                'progress_pct' => 50,
                'updated_at' => now(),
            ]);

            // 2. Inspect (dry-run)
            //    cpanel → CpanelRestoreEngine::dryRun (mevcut)
            //    directadmin → sysapi directadmin-import mode=inspect
            //    plesk → sysapi plesk-import mode=inspect
            //
            // Şimdilik tüm kaynak panellerde aynı dryRun (engine içerideki parser router
            // tar içeriğine bakıp doğru parser'a yönlendirir — multi-format support).
            $tarPath = storage_path('app/'.$localPath);
            $dryRun = $engine->dryRun($tarPath);
            $sourceUser = $dryRun['plan']['username'] ?? $this->sourceUsername;

            DB::table('migration_jobs')->where('id', $job->id)->update([
                'status' => 'planned',
                'source_username' => $sourceUser,
                'target_username' => 'onx_'.preg_replace('/[^a-z0-9_]/i', '', $sourceUser),
                'inspect_data' => json_encode($dryRun),
                'current_step' => 'planned',
                'progress_pct' => 100,
                'finished_at' => now(),
                'updated_at' => now(),
            ]);

            Log::info('bulk_pull.completed', [
                'job_id' => $job->id,
                'source' => $sourceUser,
                'tar_size_mb' => round($tarSize / 1024 / 1024, 1),
            ]);
        } catch (\Throwable $e) {
            $this->markFailed($job->id, $e->getMessage());
            Log::error('bulk_pull.failed', [
                'job_id' => $job->id,
                'source' => $this->sourceUsername,
                'error' => $e->getMessage(),
            ]);
        }
    }

    private function markFailed(int $jobId, string $error): void
    {
        DB::table('migration_jobs')->where('id', $jobId)->update([
            'status' => 'failed',
            'error_message' => $error,
            'finished_at' => now(),
            'updated_at' => now(),
        ]);
    }

    public function failed(\Throwable $exception): void
    {
        $this->markFailed($this->migrationJobId, $exception->getMessage());
    }
}
