<?php

namespace App\Support;

use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use ZipArchive;

/**
 * به‌روزرسانی اسکریپت از طریق فایل ZIP آپلودی.
 *
 * فایل zip باید شامل فایل‌های پروژه (نسبت به ریشه) باشد. قبل از استخراج،
 * یک پشتیبان از فایل‌های جایگزین‌شونده گرفته می‌شود و سپس مهاجرت‌ها اجرا می‌شوند.
 */
class ScriptUpdater
{
    /** پوشه‌هایی که هرگز نباید با آپدیت بازنویسی شوند. */
    protected static array $protected = [
        '.env', 'storage', 'vendor', '.git', 'node_modules',
    ];

    /**
     * اعمال فایل به‌روزرسانی.
     *
     * @return array{ok: bool, message: string, backup?: string}
     */
    public static function apply(string $zipPath): array
    {
        if (! class_exists(ZipArchive::class)) {
            return ['ok' => false, 'message' => 'افزونه‌ی Zip در PHP فعال نیست.'];
        }

        $zip = new ZipArchive();
        if ($zip->open($zipPath) !== true) {
            return ['ok' => false, 'message' => 'فایل ZIP معتبر نیست یا قابل باز شدن نیست.'];
        }

        $base = base_path();
        $stamp = date('Ymd_His');
        $backupDir = storage_path("app/update-backups/{$stamp}");
        File::ensureDirectoryExists($backupDir);

        $applied = [];

        for ($i = 0; $i < $zip->numEntries; $i++) {
            $name = $zip->getNameIndex($i);
            if ($name === false || str_ends_with($name, '/')) {
                continue;
            }

            // جلوگیری از Path Traversal و مسیرهای محافظت‌شده
            $relative = ltrim(str_replace('\\', '/', $name), '/');
            if (str_contains($relative, '..')) {
                continue;
            }
            $firstSegment = explode('/', $relative)[0];
            if (in_array($firstSegment, static::$protected, true)) {
                continue;
            }

            $target = $base . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relative);

            // پشتیبان از فایل موجود
            if (File::exists($target)) {
                $backupTarget = $backupDir . DIRECTORY_SEPARATOR . $relative;
                File::ensureDirectoryExists(dirname($backupTarget));
                File::copy($target, $backupTarget);
            }

            // استخراج فایل
            File::ensureDirectoryExists(dirname($target));
            $contents = $zip->getFromIndex($i);
            if ($contents !== false) {
                File::put($target, $contents);
                $applied[] = $relative;
            }
        }

        $zip->close();

        if (empty($applied)) {
            return ['ok' => false, 'message' => 'هیچ فایل قابل‌اعمالی در بسته یافت نشد.'];
        }

        // اجرای مهاجرت‌ها و پاک‌سازی کش
        try {
            Artisan::call('migrate', ['--force' => true]);
            Artisan::call('optimize:clear');
        } catch (\Throwable $e) {
            return [
                'ok' => true,
                'message' => 'فایل‌ها اعمال شد اما اجرای مهاجرت با خطا مواجه شد: ' . $e->getMessage(),
                'backup' => $backupDir,
            ];
        }

        return [
            'ok' => true,
            'message' => NumberToWords::digits(count($applied)) . ' فایل با موفقیت به‌روزرسانی شد.',
            'backup' => $backupDir,
        ];
    }
}
