<?php

namespace App\Filesystem;

use Illuminate\Filesystem\Filesystem as BaseFilesystem;

class Filesystem extends BaseFilesystem
{
    protected int $renameRetryCount = 5;

    protected int $renameRetryDelayMs = 100;

    public function replace($path, $content, $mode = null): void
    {
        clearstatcache(true, $path);

        $path = realpath($path) ?: $path;

        $tempPath = tempnam(dirname($path), basename($path));

        if (! is_null($mode)) {
            @chmod($tempPath, $mode);
        } else {
            @chmod($tempPath, 0777 - umask());
        }

        file_put_contents($tempPath, $content);

        if (PHP_OS_FAMILY === 'Windows') {
            $this->windowsRename($tempPath, $path);
        } else {
            rename($tempPath, $path);
        }
    }

    protected function windowsRename(string $source, string $destination): void
    {
        for ($attempt = 1; $attempt <= $this->renameRetryCount; $attempt++) {
            if (@rename($source, $destination)) {
                return;
            }

            if ($attempt < $this->renameRetryCount) {
                usleep($this->renameRetryDelayMs * 1000);
            }
        }

        if (@unlink($destination) && @rename($source, $destination)) {
            return;
        }

        if (file_exists($source)) {
            @unlink($source);
        }

        throw new \RuntimeException(
            "无法将文件 [{$source}] 重命名为 [{$destination}]，Windows 文件可能被其他进程占用。"
        );
    }
}
