<?php

declare(strict_types=1);

namespace SeoScope\Install;

use SeoScope\Http\Request;

final class EnvironmentChecker
{
    /** @var list<array{level: 'error'|'warning', key: string, params?: array<string, string>}> */
    private array $items = [];

    public function run(string $root): bool
    {
        $this->items = [];

        if (PHP_VERSION_ID < 80000) {
            $this->items[] = [
                'level' => 'error',
                'key' => 'install.env.php_version',
                'params' => ['version' => PHP_VERSION],
            ];
        }

        $requiredExt = ['pdo', 'pdo_mysql', 'json', 'openssl', 'mbstring', 'curl'];
        foreach ($requiredExt as $ext) {
            if (!extension_loaded($ext)) {
                $this->items[] = [
                    'level' => 'error',
                    'key' => 'install.env.extension_missing',
                    'params' => ['ext' => $ext],
                ];
            }
        }

        foreach (['storage', 'storage/cache', 'storage/logs', 'storage/sessions', 'storage/app'] as $rel) {
            $path = $root . '/' . $rel;
            if (!is_dir($path)) {
                // Try to create missing directories to avoid requiring manual steps on shared hosting.
                @mkdir($path, 0775, true);
            }
            if (!is_dir($path) || !is_writable($path)) {
                $this->items[] = [
                    'level' => 'error',
                    'key' => 'install.env.storage_not_writable',
                    'params' => ['path' => $rel],
                ];
            }
        }

        $probe = $root . '/storage/cache/.install_probe_' . bin2hex(random_bytes(4));
        $writableProbe = @file_put_contents($probe, '1') !== false;
        if ($writableProbe) {
            @unlink($probe);
        } else {
            $this->items[] = [
                'level' => 'error',
                'key' => 'install.env.probe_write_failed',
                'params' => ['path' => 'storage/cache'],
            ];
        }

        $mem = ini_get('memory_limit');
        if ($mem !== false && $mem !== '') {
            $bytes = $this->parseMemoryLimit((string) $mem);
            if ($bytes > 0 && $bytes < 128 * 1024 * 1024) {
                $this->items[] = [
                    'level' => 'warning',
                    'key' => 'install.env.memory_low',
                    'params' => ['limit' => (string) $mem],
                ];
            }
        }

        if (!Request::isHttps()) {
            $this->items[] = [
                'level' => 'warning',
                'key' => 'install.env.no_https',
                'params' => [],
            ];
        }

        foreach ($this->items as $item) {
            if ($item['level'] === 'error') {
                return false;
            }
        }
        return true;
    }

    /** @return list<array{level: 'error'|'warning', key: string, params?: array<string, string>}> */
    public function getItems(): array
    {
        return $this->items;
    }

    private function parseMemoryLimit(string $limit): int
    {
        $limit = trim($limit);
        if ($limit === '-1') {
            return PHP_INT_MAX;
        }
        $unit = strtoupper(substr($limit, -1));
        $num = (float) $limit;
        switch ($unit) {
            case 'G':
                return (int) ($num * 1024 * 1024 * 1024);
            case 'M':
                return (int) ($num * 1024 * 1024);
            case 'K':
                return (int) ($num * 1024);
            default:
                return (int) $num;
        }
    }
}
