<?php
declare(strict_types=1);

function market_json_path(): string
{
    return dirname(__DIR__) . '/data/market.json';
}

function market_item_stock(array $row): int
{
    return max(0, (int) ($row['stock'] ?? 0));
}

/**
 * @return array<int, array<string, mixed>>
 */
function market_read_list(): array
{
    $path = market_json_path();
    if (!is_readable($path)) {
        return [];
    }
    $raw = (string) file_get_contents($path);
    if (str_starts_with($raw, "\xEF\xBB\xBF")) {
        $raw = substr($raw, 3);
    }
    $list = json_decode($raw, true);

    return is_array($list) ? $list : [];
}

/**
 * @param array<int, array<string, mixed>> $list
 */
function market_write_list(array $list): bool
{
    $path = market_json_path();
    $dir = dirname($path);
    if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
        return false;
    }
    $json = json_encode(
        $list,
        JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );
    if ($json === false) {
        return false;
    }

    return file_put_contents($path, $json) !== false;
}

/**
 * @param callable(array<int, array<string, mixed>>): array{ok: bool, error?: string, item?: array<string, mixed>, stock?: int} $mutator
 * @return array{ok: bool, error?: string, item?: array<string, mixed>, stock?: int}
 */
function market_with_locked_list(callable $mutator): array
{
    $path = market_json_path();
    $dir = dirname($path);
    if (!is_dir($dir) && !mkdir($dir, 0755, true) && !is_dir($dir)) {
        return ['ok' => false, 'error' => 'market'];
    }

    $fp = @fopen($path, 'c+');
    if ($fp === false) {
        return ['ok' => false, 'error' => 'market'];
    }

    try {
        if (!flock($fp, LOCK_EX)) {
            return ['ok' => false, 'error' => 'market'];
        }

        $size = (int) filesize($path);
        $raw = $size > 0 ? (string) fread($fp, $size) : '';
        if (str_starts_with($raw, "\xEF\xBB\xBF")) {
            $raw = substr($raw, 3);
        }
        $list = json_decode($raw !== '' ? $raw : '[]', true);
        if (!is_array($list)) {
            $list = [];
        }

        $result = $mutator($list);
        if (!($result['ok'] ?? false)) {
            flock($fp, LOCK_UN);
            fclose($fp);

            return $result;
        }

        $json = json_encode(
            $list,
            JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
        );
        if ($json === false) {
            flock($fp, LOCK_UN);
            fclose($fp);

            return ['ok' => false, 'error' => 'server'];
        }

        ftruncate($fp, 0);
        rewind($fp);
        fwrite($fp, $json);
        fflush($fp);
        flock($fp, LOCK_UN);
        fclose($fp);

        return $result;
    } catch (Throwable $e) {
        if (is_resource($fp)) {
            @flock($fp, LOCK_UN);
            @fclose($fp);
        }

        return ['ok' => false, 'error' => 'server'];
    }
}

/**
 * @return array{ok: bool, error?: string, item?: array<string, mixed>, stock?: int}
 */
function market_decrement_stock(string $itemId): array
{
    $itemId = trim($itemId);
    if ($itemId === '') {
        return ['ok' => false, 'error' => 'item'];
    }

    return market_with_locked_list(static function (array &$list) use ($itemId): array {
        foreach ($list as &$row) {
            if (!is_array($row)) {
                continue;
            }
            if ((string) ($row['id'] ?? '') !== $itemId) {
                continue;
            }
            $stock = market_item_stock($row);
            if ($stock <= 0) {
                return ['ok' => false, 'error' => 'out_of_stock'];
            }
            $row['stock'] = $stock - 1;
            $item = $row;

            return [
                'ok' => true,
                'item' => $item,
                'stock' => market_item_stock($item),
            ];
        }

        return ['ok' => false, 'error' => 'not_found'];
    });
}

/**
 * @return array{ok: bool, error?: string, item?: array<string, mixed>, stock?: int}
 */
function market_increment_stock(string $itemId, int $amount = 1): array
{
    $itemId = trim($itemId);
    $amount = max(1, $amount);

    return market_with_locked_list(static function (array &$list) use ($itemId, $amount): array {
        foreach ($list as &$row) {
            if (!is_array($row)) {
                continue;
            }
            if ((string) ($row['id'] ?? '') !== $itemId) {
                continue;
            }
            $row['stock'] = market_item_stock($row) + $amount;
            $item = $row;

            return [
                'ok' => true,
                'item' => $item,
                'stock' => market_item_stock($item),
            ];
        }

        return ['ok' => false, 'error' => 'not_found'];
    });
}

/**
 * @return array<int, array<string, mixed>>
 */
function market_public_items(): array
{
    $out = [];
    foreach (market_read_list() as $row) {
        if (!is_array($row)) {
            continue;
        }
        $id = isset($row['id']) ? trim((string) $row['id']) : '';
        $title = isset($row['title']) ? trim((string) $row['title']) : '';
        if ($id === '' || $title === '') {
            continue;
        }
        $out[] = [
            'id' => $id,
            'title' => $title,
            'logo' => isset($row['logo']) ? trim((string) $row['logo']) : '',
            'description' => isset($row['description']) ? trim((string) $row['description']) : '',
            'price' => max(0, (int) ($row['price'] ?? 0)),
            'stock' => market_item_stock($row),
        ];
    }

    return $out;
}
