<?php

declare(strict_types=1);

namespace PrettyLinks\Pro\PostOptions;

/**
 * Per-post Pretty Links option storage.
 *
 * Single postmeta key `_prlipro-post-options` holding an associative array.
 * This is v3's storage slot, preserved verbatim so upgrades and downgrades
 * are lossless with no migration.
 *
 * Keys:
 *   disable_keyword_replacements  (bool)
 *   disable_url_replacements      (bool)
 *   hide_social_buttons           (bool)
 *   requested_slug                (string)
 */
class PostOptions
{
    private const META_KEY = '_prlipro-post-options';

    /**
     * Read the normalized post options blob for a post.
     *
     * @param integer $postId The post ID to read options for.
     *
     * @return array{
     *   disable_keyword_replacements: bool,
     *   disable_url_replacements: bool,
     *   hide_social_buttons: bool,
     *   requested_slug: string,
     * }
     */
    public static function get(int $postId): array
    {
        $blob = self::readBlob($postId);

        return [
            'disable_keyword_replacements' => !empty($blob['disable_keyword_replacements']),
            'disable_url_replacements'     => !empty($blob['disable_url_replacements']),
            'hide_social_buttons'          => !empty($blob['hide_social_buttons']),
            'requested_slug'               => isset($blob['requested_slug'])
                ? (string) $blob['requested_slug']
                : '',
        ];
    }

    /**
     * Persist the post options blob for a post.
     *
     * @param integer              $postId The post ID to save options for.
     * @param array<string, mixed> $opts   The raw submitted option values.
     *
     * @return void
     */
    public static function save(int $postId, array $opts): void
    {
        $blob = self::readBlob($postId);

        $blob['disable_keyword_replacements'] = !empty($opts['disable_keyword_replacements']) ? 1 : 0;
        $blob['disable_url_replacements']     = !empty($opts['disable_url_replacements']) ? 1 : 0;
        $blob['hide_social_buttons']          = !empty($opts['hide_social_buttons']) ? 1 : 0;

        // Apply `sanitize_title` per path segment so prefix-baked slugs
        // like `go/Hello World` round-trip to `go/hello-world`. A single
        // `sanitize_title()` call on the whole string would strip the `/`
        // separator; in v4 the prefix lives inside the stored slug.
        $raw   = trim((string) ($opts['requested_slug'] ?? ''), " \t\n\r\0\x0B/");
        $parts = array_filter(array_map('sanitize_title', explode('/', $raw)), static function ($p) {
            return $p !== '';
        });
        $slug  = implode('/', $parts);
        if ($slug !== '') {
            $blob['requested_slug'] = $slug;
        } else {
            unset($blob['requested_slug']);
        }

        update_post_meta($postId, self::META_KEY, $blob);
    }

    /**
     * Read the raw stored options blob for a post.
     *
     * @param integer $postId The post ID to read the blob for.
     *
     * @return array<string, mixed>
     */
    private static function readBlob(int $postId): array
    {
        $raw = get_post_meta($postId, self::META_KEY, true);
        return is_array($raw) ? $raw : [];
    }
}
