<?php

declare(strict_types=1);

/**
 * DentalCare storage-health model.
 *
 * PHP disk_free_space() reports the underlying/shared filesystem on many cPanel
 * hosts. It does not normally report the individual hosting-account quota. The
 * functions below keep those measurements separate and never describe a shared
 * filesystem value as the account's remaining quota.
 */

function dental_storage_format_bytes(int|float $bytes, int $precision = 2): string
{
    $bytes = max(0, (float)$bytes);
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'];
    $index = 0;
    while ($bytes >= 1024 && $index < count($units) - 1) {
        $bytes /= 1024;
        $index++;
    }
    return number_format($bytes, $index === 0 ? 0 : $precision) . ' ' . $units[$index];
}

function dental_storage_quota_snapshot(): array
{
    $total = (int)setting_get('storage_health', 'account_quota_bytes', '0');
    $used = (int)setting_get('storage_health', 'account_used_bytes', '0');
    $recordedAt = trim((string)setting_get('storage_health', 'account_snapshot_at', ''));
    $recordedBy = trim((string)setting_get('storage_health', 'account_snapshot_by', ''));
    $timestamp = $recordedAt !== '' ? (strtotime($recordedAt) ?: 0) : 0;
    $now = time();
    $ageSeconds = $timestamp > 0 ? max(0, $now - $timestamp) : PHP_INT_MAX;
    $validNumbers = $total > 0 && $used >= 0 && $used <= $total;
    $notFuture = $timestamp > 0 && $timestamp <= $now + 300;
    $fresh = $validNumbers && $notFuture && $ageSeconds <= 7 * 86400;
    $available = $validNumbers ? max(0, $total - $used) : 0;
    $availablePercent = $validNumbers && $total > 0 ? ($available / $total) * 100 : 0.0;

    return [
        'configured' => $validNumbers,
        'fresh' => $fresh,
        'total_bytes' => $total,
        'used_bytes' => $used,
        'available_bytes' => $available,
        'available_percent' => $availablePercent,
        'recorded_at' => $recordedAt,
        'recorded_by' => $recordedBy,
        'age_seconds' => $ageSeconds,
    ];
}

function dental_storage_record_quota_snapshot(float $usedGb, float $totalGb): array
{
    if (!is_finite($usedGb) || !is_finite($totalGb)) {
        throw new RuntimeException('Enter valid numeric disk values from cPanel.');
    }
    if ($totalGb <= 0 || $totalGb > 1048576) {
        throw new RuntimeException('The hosting quota must be greater than zero.');
    }
    if ($usedGb < 0 || $usedGb > $totalGb) {
        throw new RuntimeException('Used disk space cannot be negative or greater than the hosting quota.');
    }

    $total = (int)round($totalGb * 1073741824);
    $used = (int)round($usedGb * 1073741824);
    $user = current_user() ?: [];
    setting_set('storage_health', 'account_quota_bytes', (string)$total);
    setting_set('storage_health', 'account_used_bytes', (string)$used);
    setting_set('storage_health', 'account_snapshot_at', date('c'));
    setting_set('storage_health', 'account_snapshot_by', (string)($user['name'] ?? $user['email'] ?? 'Superuser'));
    setting_set('production_acceptance', 'automated_validation_evidence', '');

    return dental_storage_quota_snapshot();
}

function dental_storage_clear_quota_snapshot(): void
{
    foreach (['account_quota_bytes', 'account_used_bytes', 'account_snapshot_at', 'account_snapshot_by'] as $key) {
        setting_set('storage_health', $key, '');
    }
    setting_set('production_acceptance', 'automated_validation_evidence', '');
}

function dental_storage_write_probe(): array
{
    $directory = DENTAL_ROOT . '/storage/runtime';
    if (!is_dir($directory) && !@mkdir($directory, 0750, true)) {
        return ['ready' => false, 'detail' => 'Runtime storage directory could not be created.'];
    }
    if (!is_writable($directory)) {
        return ['ready' => false, 'detail' => 'Runtime storage directory is not writable.'];
    }

    $path = @tempnam($directory, 'dc-storage-probe-');
    if (!is_string($path) || $path === '') {
        return ['ready' => false, 'detail' => 'DentalCare could not create a temporary storage probe.'];
    }

    $ready = false;
    $written = 0;
    try {
        $handle = @fopen($path, 'wb');
        if ($handle !== false) {
            $payload = str_repeat('D', 65536);
            $written = (int)@fwrite($handle, $payload);
            @fflush($handle);
            if (function_exists('fsync')) @fsync($handle);
            @fclose($handle);
            clearstatcache(true, $path);
            $ready = $written === strlen($payload) && is_file($path) && (int)(filesize($path) ?: 0) === strlen($payload);
        }
    } finally {
        @unlink($path);
    }

    return [
        'ready' => $ready,
        'written_bytes' => $written,
        'detail' => $ready
            ? 'DentalCare successfully created, flushed, verified, and removed a protected runtime test file.'
            : 'DentalCare could not complete a protected runtime write test.',
    ];
}

function dental_storage_bounded_directory_usage(string $path, float $deadline, int $maxFiles = 25000): array
{
    $bytes = 0;
    $files = 0;
    $partial = false;
    if (!is_dir($path)) return ['bytes' => 0, 'files' => 0, 'partial' => false];

    try {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
            RecursiveIteratorIterator::LEAVES_ONLY,
            RecursiveIteratorIterator::CATCH_GET_CHILD
        );
        foreach ($iterator as $item) {
            if (microtime(true) >= $deadline || $files >= $maxFiles) {
                $partial = true;
                break;
            }
            if (!$item instanceof SplFileInfo || !$item->isFile() || $item->isLink()) continue;
            $bytes += max(0, (int)$item->getSize());
            $files++;
        }
    } catch (Throwable) {
        $partial = true;
    }

    return ['bytes' => $bytes, 'files' => $files, 'partial' => $partial];
}

function dental_storage_managed_usage(): array
{
    static $cache = null;
    if (is_array($cache)) return $cache;

    $targets = [
        'Backups' => DENTAL_ROOT . '/storage/backups',
        'Generated installers' => DENTAL_ROOT . '/storage/generated-installers',
        'Runtime logs' => DENTAL_ROOT . '/storage/runtime/logs',
        'Runtime sessions' => DENTAL_ROOT . '/storage/runtime/sessions',
        'Import archives' => DENTAL_ROOT . '/storage/private/imports',
        'Public uploads' => DENTAL_ROOT . '/public/uploads',
    ];
    $deadline = microtime(true) + 0.75;
    $rows = [];
    $totalBytes = 0;
    $totalFiles = 0;
    $partial = false;
    foreach ($targets as $label => $path) {
        $usage = dental_storage_bounded_directory_usage($path, $deadline);
        $rows[] = [
            'label' => $label,
            'path' => str_replace(DENTAL_ROOT . '/', '', $path),
            'bytes' => (int)$usage['bytes'],
            'files' => (int)$usage['files'],
            'partial' => !empty($usage['partial']),
        ];
        $totalBytes += (int)$usage['bytes'];
        $totalFiles += (int)$usage['files'];
        $partial = $partial || !empty($usage['partial']);
    }

    $cache = [
        'rows' => $rows,
        'total_bytes' => $totalBytes,
        'total_files' => $totalFiles,
        'partial' => $partial,
    ];
    return $cache;
}

function dental_storage_health_snapshot(): array
{
    $quota = dental_storage_quota_snapshot();
    $filesystemFreeRaw = @disk_free_space(DENTAL_ROOT);
    $filesystemTotalRaw = @disk_total_space(DENTAL_ROOT);
    $filesystemFree = is_float($filesystemFreeRaw) || is_int($filesystemFreeRaw) ? max(0, (int)$filesystemFreeRaw) : 0;
    $filesystemTotal = is_float($filesystemTotalRaw) || is_int($filesystemTotalRaw) ? max(0, (int)$filesystemTotalRaw) : 0;
    // Some virtual/shared filesystems return sentinel-sized values rather than a
    // usable capacity. Treat impossible or internally inconsistent values as
    // unavailable instead of displaying an absurd amount of free space.
    $maximumReasonableFilesystem = 16 * 1125899906842624; // 16 PiB
    if ($filesystemTotal <= 0 || $filesystemFree < 0 || $filesystemFree > $filesystemTotal || $filesystemTotal > $maximumReasonableFilesystem) {
        $filesystemFree = 0;
        $filesystemTotal = 0;
    }
    $filesystemPercent = $filesystemTotal > 0 ? ($filesystemFree / $filesystemTotal) * 100 : 0.0;
    $writeProbe = dental_storage_write_probe();
    $managed = dental_storage_managed_usage();

    $source = !empty($quota['fresh']) ? 'hosting_quota' : 'shared_filesystem';
    $status = 'pass';
    $metric = '';
    $detail = '';

    if (empty($writeProbe['ready'])) {
        $status = 'blocked';
        $metric = 'Write probe failed';
        $detail = (string)$writeProbe['detail'] . ' Sessions, uploads, logs, backups, and updates may fail until storage permissions or hosting capacity are corrected.';
    } elseif ($source === 'hosting_quota') {
        $available = (int)$quota['available_bytes'];
        $percent = (float)$quota['available_percent'];
        $status = ($available < 536870912 || $percent < 5)
            ? 'blocked'
            : (($available < 1073741824 || $percent < 10) ? 'review' : 'pass');
        $metric = dental_storage_format_bytes($available) . ' account quota available';
        $detail = 'This result uses the current cPanel quota snapshot: '
            . dental_storage_format_bytes((int)$quota['used_bytes']) . ' used of '
            . dental_storage_format_bytes((int)$quota['total_bytes']) . '. The protected write probe also passed.';
    } elseif ($filesystemTotal > 0 && $filesystemFree > 0) {
        // Shared-hosting filesystem values are not the cPanel account quota.
        // A server-wide free-space percentage can be tiny on a very large shared
        // volume even when several gigabytes remain and the account itself is
        // healthy. Therefore percentage is informational only here. Production
        // is blocked only by a failed write probe or an absolute emergency floor.
        $status = $filesystemFree < 134217728
            ? 'blocked'
            : ($filesystemFree < 536870912 ? 'review' : 'pass');
        $metric = dental_storage_format_bytes($filesystemFree) . ' shared filesystem available';
        $detail = 'The protected write probe passed. PHP cannot read this cPanel account’s individual quota, so the shared-server filesystem value and percentage are informational only. They are not used as the account’s remaining quota.';
        if (!empty($quota['configured']) && empty($quota['fresh'])) {
            $detail .= ' The saved cPanel quota snapshot is older than seven days; record a current snapshot to display account-level capacity.';
        }
    } else {
        // Unknown capacity is not a failure when DentalCare can prove that its
        // protected storage path is writable. Do not block production merely
        // because shared hosting hides account-level quota APIs from PHP.
        $status = 'pass';
        $metric = 'Write-safe; account quota unknown';
        $detail = 'The protected write probe passed. PHP could not read filesystem capacity and no current cPanel quota snapshot is recorded. Capacity is unknown—not low—and does not block production.';
    }

    return [
        'status' => $status,
        'source' => $source,
        'metric' => $metric,
        'detail' => $detail,
        'checked_path' => DENTAL_ROOT,
        'quota' => $quota,
        'filesystem' => [
            'free_bytes' => $filesystemFree,
            'total_bytes' => $filesystemTotal,
            'free_percent' => $filesystemPercent,
        ],
        'write_probe' => $writeProbe,
        'managed' => $managed,
        'generated_at' => date('c'),
    ];
}
