<?php

declare(strict_types=1);

namespace App\Jobs;

use App\Domain\Migration\Services\CpanelImporter;
use App\Domain\Migration\Services\CpanelRestoreEngine;
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;

class RunCpanelMigrationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $timeout = 3600; // 1 hour

    public int $tries = 1;

    /**
     * @param  bool  $resume  If true, picks up after migration_jobs.last_completed_step.
     *                       Used when a previous run failed and admin clicked "Retry".
     */
    public function __construct(
        public readonly int $migrationJobId,
        public readonly bool $resume = false,
    ) {}

    public function handle(CpanelRestoreEngine $engine, CpanelImporter $importer): void
    {
        $job = DB::table('migration_jobs')->find($this->migrationJobId);

        if (! $job) {
            return;
        }

        $tarPath = storage_path('app/'.$job->tar_filename);
        $options = $job->options ? json_decode($job->options, true) : [];

        DB::table('migration_jobs')->where('id', $job->id)->update([
            'status' => 'restoring',
            'started_at' => now(),
            'updated_at' => now(),
        ]);

        $startedAt = microtime(true);

        try {
            // Prefer sysapi-driven import (Phase 1.5+); fall back to in-process
            // engine if the target account hasn't been provisioned yet.
            $accountId = DB::table('accounts')
                ->where('username', $job->target_username)
                ->value('id');

            if ($accountId && function_exists('proc_open')) {
                DB::table('migration_jobs')->where('id', $job->id)->update([
                    'current_step' => 'sysapi_import',
                    'progress_pct' => 10,
                    'updated_at' => now(),
                ]);

                $skip = $this->mappingToSkip($options);
                $result = $importer->execute($tarPath, [
                    'target_username' => $job->target_username,
                    'account_id' => (int) $accountId,
                    'skip' => $skip,
                ]);

                DB::table('migration_jobs')->where('id', $job->id)->update([
                    'status' => 'completed',
                    'progress_pct' => 100,
                    'current_step' => 'done',
                    'finished_at' => now(),
                    'duration_seconds' => (int) (microtime(true) - $startedAt),
                    'warnings' => json_encode($result['warnings'] ?? []),
                    'updated_at' => now(),
                ]);

                return;
            }

            // Legacy in-process path (no account row yet — full provisioning).
            // Retry support: $resume=true ise last_completed_step'ten sonra başlar.
            // Step sıralaması engine'in step adlarıyla aynı olmalı:
            //   extract, parse, account, home, databases, dns, mail, ssl, cron, limits, done
            $stepOrder = ['extract','parse','account','home','databases','dns','mail','ssl','cron','limits'];
            $resumeFrom = $this->resume && $job->last_completed_step
                ? (array_search($job->last_completed_step, $stepOrder, true) + 1)
                : 0;

            $result = $engine->execute(
                $tarPath,
                [
                    'target_username' => $job->target_username,
                    'target_package_id' => $job->target_package_id,
                    'restore_home' => $options['restore_home'] ?? true,
                    'restore_dbs' => $options['restore_dbs'] ?? true,
                    'restore_dns' => $options['restore_dns'] ?? true,
                    'restore_mail' => $options['restore_mail'] ?? false,
                    'restore_ssl' => $options['restore_ssl'] ?? true,
                    'restore_cron' => $options['restore_cron'] ?? true,
                    'overwrite_existing' => $options['overwrite_existing'] ?? false,
                    // Engine bu step'lerden önce gelenleri skip eder (retry semantiği)
                    'skip_until_index' => $resumeFrom,
                    'step_order' => $stepOrder,
                ],
                function (string $step, int $pct, string $msg) use ($job, $stepOrder) {
                    // step tamamlandığında last_completed_step yaz
                    $idx = array_search($step, $stepOrder, true);
                    $update = [
                        'current_step' => $step,
                        'progress_pct' => $pct,
                        'updated_at' => now(),
                    ];
                    // 'done' veya step başlangıcı değil, step tamamlandı sinyali pct=100/step+1'de
                    // Engine'in step callback contract'ı "this step is now done"; pct==100/N olarak gel
                    // bizim approximate yöntem: pct verisi step ileri sayma demektir.
                    if ($idx !== false && $idx > 0) {
                        $update['last_completed_step'] = $stepOrder[$idx - 1] ?? null;
                    }
                    DB::table('migration_jobs')->where('id', $job->id)->update($update);
                }
            );

            DB::table('migration_jobs')->where('id', $job->id)->update([
                'status' => 'completed',
                'progress_pct' => 100,
                'current_step' => 'done',
                'finished_at' => now(),
                'duration_seconds' => $result['duration_seconds'],
                'warnings' => json_encode($result['warnings']),
                'updated_at' => now(),
            ]);

        } catch (\Throwable $e) {
            DB::table('migration_jobs')->where('id', $job->id)->update([
                'status' => 'failed',
                'finished_at' => now(),
                'error_message' => $e->getMessage(),
                'updated_at' => now(),
            ]);

            throw $e;
        }
    }

    /**
     * Translate the UI's restore_<section>=false toggles into the bash
     * script's --skip flags.
     *
     * @return array<string>
     */
    private function mappingToSkip(array $options): array
    {
        $map = [
            'homedir' => 'restore_home',
            'mysql' => 'restore_dbs',
            'dns' => 'restore_dns',
            'email' => 'restore_mail',
            'ssl' => 'restore_ssl',
            'cron' => 'restore_cron',
        ];

        $skip = [];
        foreach ($map as $section => $optionKey) {
            // Mail defaults to false (opt-in), others default to true.
            $default = $optionKey === 'restore_mail' ? false : true;
            if (! ($options[$optionKey] ?? $default)) {
                $skip[] = $section;
            }
        }

        return $skip;
    }
}
