<?php

declare(strict_types=1);

namespace App\Domain\System\Services;

use App\Domain\Sysapi\Contracts\SysapiBridge;
use App\Domain\Sysapi\Contracts\SysapiResult;
use App\Domain\Sysapi\Exceptions\SysapiException;

/**
 * Sistem ve panel güncelleme yönetimi.
 *
 * Production'da:
 *   - runSystemUpdate  → onx-system-update bash script (dnf)
 *   - runPanelUpdate   → onx-panel-update bash script (git pull + composer + npm + migrate)
 *   - checkUpdates     → Phase 2'de onx-check-updates script'ine bağlanacak (dnf check-update)
 *
 * Master Plan §5 — Sysapi Layer.
 */
final class UpdateManager
{
    public function __construct(
        private readonly SysapiBridge $sysapi,
    ) {}

    /**
     * Mevcut paket güncelleme durumunu kontrol et — gerçek `dnf check-update` çağrısı.
     *
     * Sysapi('check-updates') script'i `dnf check-update --refresh` çalıştırır,
     * exit code 100 = güncellemeler var, 0 = yok. Onoxsoft panel için GitHub API'sinden
     * en son release tag'i çekilir.
     *
     * @return array{
     *   checked_at: string,
     *   dnf_packages_available: int,
     *   packages: array<int, array{name:string,current:string,available:string,size_kb:int}>,
     *   onox_update_available: bool,
     *   onox_current_version: string,
     *   onox_latest_version: string|null,
     *   reboot_required: bool
     * }
     *
     * @throws SysapiException
     */
    public function checkUpdates(): array
    {
        $result = $this->sysapi->run(
            command: 'check-updates',
            args: [],
            timeoutSeconds: 60,
        );

        $data = $result->data ?? [];
        $currentVersion = (string) config('app.version', '0.1.0-dev');
        $latestVersion = $this->fetchPanelLatestVersion();

        return [
            'checked_at' => now()->toIso8601String(),
            'dnf_packages_available' => (int) ($data['count'] ?? 0),
            'packages' => (array) ($data['packages'] ?? []),
            'onox_update_available' => $latestVersion !== null && version_compare($latestVersion, $currentVersion, '>'),
            'onox_current_version' => $currentVersion,
            'onox_latest_version' => $latestVersion,
            'reboot_required' => (bool) ($data['reboot_required'] ?? false),
        ];
    }

    /**
     * GitHub API'sinden Onoxsoft panel'in en son release tag'ini çek.
     * Network hatasında veya rate limit'te null döner — UI bunu "bilinmiyor" olarak gösterir.
     */
    public function fetchPanelLatestVersion(): ?string
    {
        try {
            // HTTP'yi runtime'da çöz ki test ortamında Http::fake() sırf bu method için override edilebilsin
            $response = \Illuminate\Support\Facades\Http::timeout(5)
                ->withHeaders(['Accept' => 'application/vnd.github+json'])
                ->get(config('onoxsoft.update_check_url', 'https://api.github.com/repos/onoxsoft/panel/releases/latest'));

            if (! $response->successful()) {
                return null;
            }

            $tag = $response->json('tag_name');
            if (! is_string($tag) || $tag === '') {
                return null;
            }

            return ltrim($tag, 'v');
        } catch (\Throwable $e) {
            return null;
        }
    }

    /**
     * Sistem paketlerini güncelle (dnf).
     *
     * @param  string[]  $packages  Boş dizi = tüm paketleri güncelle
     * @param  bool  $securityOnly  Sadece güvenlik güncellemeleri
     * @return array{
     *   updated_count: int,
     *   packages_updated: string[],
     *   reboot_required: bool,
     *   output_tail: string
     * }
     *
     * @throws SysapiException
     */
    public function runSystemUpdate(array $packages = [], bool $securityOnly = true): array
    {
        // Validate package names (whitelist — prevent injection)
        foreach ($packages as $pkg) {
            if (! preg_match('/^[a-zA-Z0-9._-]+$/', $pkg)) {
                throw new \InvalidArgumentException("Geçersiz paket adı: {$pkg}");
            }
        }

        // dnf can take 2-10 minutes for a full update
        $timeout = empty($packages) ? 600 : 180;

        $result = $this->sysapi->run(
            command: 'system-update',
            args: [
                'packages' => array_values($packages),
                'security_only' => $securityOnly,
            ],
            timeoutSeconds: $timeout,
        );

        return $this->parseSystemUpdateData($result);
    }

    /**
     * Onoxsoft panel'ini güncelle (git + composer + npm + migrate).
     *
     * @param  string  $branch  Git branch (varsayılan: main)
     * @param  string|null  $version  Belirli bir tag'e checkout (ör: v0.18.5). Null = branch pull.
     * @return array{
     *   old_version: string,
     *   new_version: string,
     *   migrations_run: int,
     *   success: bool
     * }
     *
     * @throws SysapiException
     */
    public function runPanelUpdate(string $branch = 'main', ?string $version = null): array
    {
        // Validate branch (alphanumeric + slash + dot + hyphen, no path traversal)
        if (! preg_match('/^[a-zA-Z0-9._\/-]{1,100}$/', $branch) || str_contains($branch, '..')) {
            throw new \InvalidArgumentException("Geçersiz branch: {$branch}");
        }

        // Validate version tag if provided
        if ($version !== null) {
            if (! preg_match('/^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$/', $version)) {
                throw new \InvalidArgumentException("Geçersiz versiyon formatı: {$version}");
            }
        }

        // Panel update can take 3-8 minutes (composer + npm build)
        $result = $this->sysapi->run(
            command: 'panel-update',
            args: [
                'branch' => $branch,
                'version' => $version,
            ],
            timeoutSeconds: 480,
        );

        return $this->parsePanelUpdateData($result);
    }

    // ─── Private helpers ────────────────────────────────────────────────────

    /**
     * @return array{updated_count:int, packages_updated:string[], reboot_required:bool, output_tail:string}
     */
    private function parseSystemUpdateData(SysapiResult $result): array
    {
        if ($result->data !== null) {
            return [
                'updated_count' => (int) ($result->data['updated_count'] ?? 0),
                'packages_updated' => (array) ($result->data['packages_updated'] ?? []),
                'reboot_required' => (bool) ($result->data['reboot_required'] ?? false),
                'output_tail' => (string) ($result->data['output_tail'] ?? ''),
            ];
        }

        return [
            'updated_count' => 0,
            'packages_updated' => [],
            'reboot_required' => false,
            'output_tail' => '',
        ];
    }

    /**
     * @return array{old_version:string, new_version:string, migrations_run:int, success:bool}
     */
    private function parsePanelUpdateData(SysapiResult $result): array
    {
        if ($result->data !== null) {
            return [
                'old_version' => (string) ($result->data['old_version'] ?? 'unknown'),
                'new_version' => (string) ($result->data['new_version'] ?? 'unknown'),
                'migrations_run' => (int) ($result->data['migrations_run'] ?? 0),
                'success' => (bool) ($result->data['success'] ?? true),
            ];
        }

        return [
            'old_version' => 'unknown',
            'new_version' => 'unknown',
            'migrations_run' => 0,
            'success' => true,
        ];
    }
}
