<?php

namespace classes;

use RuntimeException;

class application_write_freeze
{
    public static function freeze(string $reason, string $owner, int $ttlSeconds = 300): void
    {
        $payload = [
            'reason' => $reason,
            'owner' => $owner,
            'created_at' => date('c'),
            'expires_at' => date('c', time() + max(30, $ttlSeconds)),
        ];

        self::writeState($payload);
    }

    public static function unfreeze(?string $owner = null): void
    {
        $state = self::state();
        if ($owner !== null && isset($state['owner']) && $state['owner'] !== $owner) {
            return;
        }

        $path = self::statePath();
        if (is_file($path)) {
            @unlink($path);
        }
    }

    public static function state(): array
    {
        $path = self::statePath();
        if (!is_file($path)) {
            return [];
        }

        $state = json_decode((string)file_get_contents($path), true);
        if (!is_array($state)) {
            @unlink($path);
            return [];
        }

        $expiresAt = strtotime((string)($state['expires_at'] ?? ''));
        if ($expiresAt !== false && $expiresAt < time()) {
            @unlink($path);
            return [];
        }

        return $state;
    }

    public static function isFrozen(): bool
    {
        return self::state() !== [];
    }

    public static function shouldBlock(string $method, string $uri, bool $isCronOrCli): bool
    {
        if (!self::isFrozen()) {
            return false;
        }

        if ($isCronOrCli) {
            return true;
        }

        $method = strtoupper($method);
        if (in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
            return false;
        }

        $path = parse_url($uri, PHP_URL_PATH) ?: '';
        return !str_starts_with($path, '/superuser/replication');
    }

    private static function writeState(array $state): void
    {
        $path = self::statePath();
        $dir = dirname($path);
        if (!is_dir($dir) && !mkdir($dir, 0770, true) && !is_dir($dir)) {
            throw new RuntimeException('Could not create write-freeze directory.');
        }

        $tempPath = tempnam($dir, 'write-freeze-');
        if ($tempPath === false) {
            throw new RuntimeException('Could not create write-freeze temp file.');
        }

        try {
            file_put_contents($tempPath, json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL, LOCK_EX);
            if (!rename($tempPath, $path)) {
                throw new RuntimeException('Could not atomically replace write-freeze state.');
            }
        } finally {
            if (is_file($tempPath)) {
                @unlink($tempPath);
            }
        }
    }

    private static function statePath(): string
    {
        $root = defined('WD') ? WD : dirname(__DIR__);
        return $root . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'application-write-freeze.json';
    }
}
