<?php

namespace App\Support\License;

use App\Models\Config;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use ZipArchive;

class Updater
{
    protected LicenseManager $license;

    public function __construct(LicenseManager $license)
    {
        $this->license = $license;
    }

    public static function updaterZipPath(): string
    {
        return storage_path('app/updater.zip');
    }

    public static function manualExtractDoneMarkerPath(): string
    {
        return storage_path('app/.updater_extract_done');
    }

    public function checkUpdate(): array
    {
        $currentVersion = $this->getCurrentVersion();
        return $this->license->checkUpdate($currentVersion);
    }

    public function getLatestVersionInfo(): array
    {
        return $this->license->getLatestVersion();
    }

    public static function normalizeVersion(string $value): string
    {
        $value = trim($value);
        $value = trim($value, '"');
        return $value;
    }

    public function getCurrentVersion(): ?string
    {
        if (function_exists('settingHelper')) {
            $v = settingHelper('version_code');
            if ($v !== null && $v !== '') {
                $normalized = static::normalizeVersion((string) $v);
                if ($normalized !== '') {
                    return $normalized;
                }
            }
        }
        return config('license.current_version');
    }

    public static function stripChangelogHtml(?string $changelog): string
    {
        if ($changelog === null || $changelog === '') {
            return '';
        }
        $text = strip_tags($changelog);
        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        return trim($text);
    }

    public function runUpdate(string $updateId, ?string $versionFromServer = null): array
    {
        $zipPath = static::updaterZipPath();
        $download = $this->license->downloadUpdateFile($updateId, 'main', $zipPath);
        if (! $download['success']) {
            return ['success' => false, 'message' => $download['message']];
        }

        if (! file_exists($zipPath)) {
            return ['success' => false, 'message' => __('Update file not found after download.')];
        }

        $zip = new ZipArchive;
        if ($zip->open($zipPath) !== true) {
            @unlink($zipPath);
            return ['success' => false, 'message' => __('Update zip could not be opened.')];
        }
        $zip->extractTo(base_path('/'));
        $zip->close();
        @unlink($zipPath);

        $hasVersionFromRequest = $versionFromServer !== null && trim((string) $versionFromServer) !== '';
        $newVersion = $hasVersionFromRequest ? trim((string) $versionFromServer) : null;
        $versionCode = $newVersion;

        $configPath = base_path('config.json');
        $config = null;
        if (file_exists($configPath)) {
            $config = json_decode(file_get_contents($configPath), true);
        }

        if (is_array($config)) {
            if ($newVersion === null) {
                $newVersion = $config['web_version'] ?? $config['app_version'] ?? null;
            }
            if (! $hasVersionFromRequest) {
                $versionCode = $config['version_code'] ?? $config['web_version_code'] ?? $config['app_version_code'] ?? $newVersion;
            }
            if (! empty($config['removed_directories']) && is_array($config['removed_directories'])) {
                foreach ($config['removed_directories'] as $dir) {
                    $path = base_path($dir);
                    if (is_dir($path)) {
                        File::deleteDirectory($path);
                    }
                }
            }
        }

        if ($newVersion !== null) {
            $this->saveVersion($newVersion, $versionCode ?? $newVersion);
        }

        if (function_exists('app_config')) {
            try {
                app_config();
            } catch (\Throwable $e) {
            }
        }
        if (function_exists('pwa_config')) {
            try {
                pwa_config();
            } catch (\Throwable $e) {
            }
        }

        Artisan::call('migrate', ['--force' => true]);
        if (class_exists(\App\Console\Commands\AllClear::class)) {
            Artisan::call('all:clear');
        } else {
            Artisan::call('config:clear');
            Artisan::call('cache:clear');
            Artisan::call('view:clear');
        }

        return [
            'success' => true,
            'message' => __('Application was successfully updated.'),
            'version' => $newVersion,
        ];
    }

    public function downloadOnly(string $updateId): array
    {
        $zipPath = static::updaterZipPath();
        $download = $this->license->downloadUpdateFile($updateId, 'main', $zipPath);
        if (! $download['success']) {
            return ['success' => false, 'message' => $download['message']];
        }
        return ['success' => true, 'message' => __('Update files downloaded.')];
    }

    public function extractOnly(?string $versionFromServer = null): array
    {
        $zipPath = static::updaterZipPath();
        if (! file_exists($zipPath)) {
            return ['success' => false, 'message' => __('Update file not found. Run step 1 first.')];
        }
        $zip = new ZipArchive;
        if ($zip->open($zipPath) !== true) {
            return ['success' => false, 'message' => __('Update zip could not be opened.')];
        }
        $zip->extractTo(base_path('/'));
        $zip->close();

        $hasVersionFromRequest = $versionFromServer !== null && trim((string) $versionFromServer) !== '';
        $newVersion = $hasVersionFromRequest ? trim((string) $versionFromServer) : null;
        $versionCode = $newVersion;

        $configPath = base_path('config.json');
        $config = null;
        if (file_exists($configPath)) {
            $config = json_decode(file_get_contents($configPath), true);
        }

        if (is_array($config)) {
            if ($newVersion === null) {
                $newVersion = $config['web_version'] ?? $config['app_version'] ?? null;
                $versionCode = $config['version_code'] ?? $config['web_version_code'] ?? $config['app_version_code'] ?? $newVersion;
            }
            if (! empty($config['removed_directories']) && is_array($config['removed_directories'])) {
                foreach ($config['removed_directories'] as $dir) {
                    $path = base_path($dir);
                    if (is_dir($path)) {
                        File::deleteDirectory($path);
                    }
                }
            }
        }

        if ($newVersion !== null) {
            $this->saveVersion($newVersion, $versionCode ?? $newVersion);
        }
        file_put_contents(static::manualExtractDoneMarkerPath(), (string) time(), LOCK_EX);
        return ['success' => true, 'message' => __('System files updated.')];
    }

    protected function saveVersion(string $version, string $versionCode): void
    {
        $code = Config::where('title', 'version_code')->first();
        if ($code) {
            $code->update(['value' => $versionCode]);
        } else {
            Config::create(['title' => 'version_code', 'value' => $versionCode]);
        }
        $ver = Config::where('title', 'current_version')->first();
        if ($ver) {
            $ver->update(['value' => $version]);
        } else {
            Config::create(['title' => 'current_version', 'value' => $version]);
        }
    }
}
