<?php

declare(strict_types=1);

namespace PrettyLinks\Pro\PostOptions;

// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
// phpcs:disable WordPress.Security.NonceVerification.Recommended
// $_SERVER values (REMOTE_ADDR, HTTP_USER_AGENT, REQUEST_URI, etc.) are read for
// click tracking / targeting / UI rendering, not form-submission input. State-changing
// operations in this class protect with wp_verify_nonce / check_admin_referer.
use PrettyLinks\Pro\Options\Store as OptionsStore;
use PrettyLinks\Repositories\Links as LinksRepository;
use PrettyLinks\Slug\Generator as SlugGenerator;

/**
 * Sidebar meta box on WP post-edit screens.
 *
 * Shown on posts, pages, and any CPT configured for auto-create or
 * keyword/URL replacement. Exposes four per-post overrides:
 *
 *   - Disable keyword replacements on this post
 *   - Disable URL replacements on this post
 *   - Hide social share buttons on this post
 *   - Requested slug for the auto-created pretty link
 *
 * Data is stored and read via {@see PostOptions} which handles v3 compat.
 *
 * Note: the save hook fires from `save_post`, which in WordPress runs
 * before `transition_post_status`. This means the requested-slug meta is
 * written before AutoCreator reads it, so the slug is honoured on first
 * publish via the classic editor. The block editor submits meta boxes via
 * a separate iframe after the REST save, so the slug applies starting from
 * the post's next save there.
 */
class MetaBox
{
    private const NONCE_ACTION = 'prli_post_options_save';
    private const NONCE_FIELD  = '_prli_post_options_nonce';

    /**
     * The Pro options store.
     *
     * @var OptionsStore
     */
    private OptionsStore $options;

    /**
     * Store the options dependency.
     *
     * @param OptionsStore $options The Pro options store.
     */
    public function __construct(OptionsStore $options)
    {
        $this->options = $options;
    }

    /**
     * Register the Pretty Links meta box on the configured post types.
     *
     * @return void
     */
    public function register(): void
    {
        foreach ($this->postTypes() as $postType) {
            add_meta_box(
                'prli-post-options',
                'Pretty Links',
                [$this, 'render'],
                $postType,
                'side',
                'default'
            );
        }
    }

    /**
     * Render the meta box markup for a post.
     *
     * @param \WP_Post $post The post being edited.
     *
     * @return void
     */
    public function render(\WP_Post $post): void
    {
        wp_nonce_field(self::NONCE_ACTION, self::NONCE_FIELD);
        $opts      = PostOptions::get((int) $post->ID);
        $linkId    = (int) get_post_meta((int) $post->ID, '_pretty-link', true);
        $prettyUrl = '';
        $linkSlug  = '';
        if ($linkId > 0) {
            $link = (new LinksRepository())->find($linkId);
            if (is_array($link)) {
                $linkSlug = (string) ($link['slug'] ?? '');
                if (!empty($link['pretty_url'])) {
                    $prettyUrl = (string) $link['pretty_url'];
                }
            }
        }

        // Field pre-fill order:
        // 1. current link's slug (lets users rename in place)
        // 2. their previously-typed-but-not-yet-applied requested slug
        // 3. a freshly suggested slug from the generator
        // Persisting the suggestion on save is fine — the save path stores
        // whatever's in the field as the requested slug and AutoCreator
        // uses it when it creates the link.
        if ($linkSlug !== '') {
            $slugFieldValue = $linkSlug;
        } elseif ((string) $opts['requested_slug'] !== '') {
            $slugFieldValue = (string) $opts['requested_slug'];
        } else {
            $slugFieldValue = SlugGenerator::generate(null, 'admin');
        }
        ?>
        <div class="prli-meta-box">
            <?php if ($prettyUrl !== '') : ?>
            <p>
                <strong><?php esc_html_e('Pretty Link:', 'pretty-link-pro'); ?></strong><br/>
                <a href="<?php echo esc_url($prettyUrl); ?>" target="_blank" rel="noreferrer noopener">
                    <?php echo esc_html($prettyUrl); ?>
                </a>
            </p>
            <?php endif; ?>
            <p>
                <label>
                    <input
                        type="checkbox"
                        name="prli_disable_keyword_replacements"
                        value="1"
                        <?php checked($opts['disable_keyword_replacements']); ?>
                    />
                    <?php esc_html_e('Disable keyword replacements', 'pretty-link-pro'); ?>
                </label>
            </p>
            <p>
                <label>
                    <input
                        type="checkbox"
                        name="prli_disable_url_replacements"
                        value="1"
                        <?php checked($opts['disable_url_replacements']); ?>
                    />
                    <?php esc_html_e('Disable URL replacements', 'pretty-link-pro'); ?>
                </label>
            </p>
            <p>
                <label>
                    <input
                        type="checkbox"
                        name="prli_hide_social_buttons"
                        value="1"
                        <?php checked($opts['hide_social_buttons']); ?>
                    />
                    <?php esc_html_e('Hide social buttons', 'pretty-link-pro'); ?>
                </label>
            </p>
            <p>
                <label for="prli_requested_slug">
                    <?php esc_html_e('Auto-link slug', 'pretty-link-pro'); ?>
                </label><br/>
                <input
                    type="text"
                    id="prli_requested_slug"
                    name="prli_requested_slug"
                    class="widefat"
                    value="<?php echo esc_attr($slugFieldValue); ?>"
                    placeholder="<?php esc_attr_e('auto-generated', 'pretty-link-pro'); ?>"
                />
                <span class="description">
                    <?php
                    echo esc_html(
                        $linkSlug !== ''
                            ? __('Change to rename the existing pretty link.', 'pretty-link-pro')
                            : __('Slug for the auto-created pretty link. Applied on the next save.', 'pretty-link-pro')
                    );
                    ?>
                </span>
            </p>
        </div>
        <?php
    }

    /**
     * Persist the submitted meta box values for a post.
     *
     * @param integer $postId The ID of the post being saved.
     *
     * @return void
     */
    public function save(int $postId): void
    {
        if (
            !isset($_POST[self::NONCE_FIELD]) ||
            !wp_verify_nonce(
                sanitize_key((string) $_POST[self::NONCE_FIELD]),
                self::NONCE_ACTION
            )
        ) {
            return;
        }
        if (!current_user_can('edit_post', $postId)) {
            return;
        }
        if (wp_is_post_revision($postId) || wp_is_post_autosave($postId)) {
            return;
        }

        PostOptions::save($postId, [
            'disable_keyword_replacements' => !empty($_POST['prli_disable_keyword_replacements']),
            'disable_url_replacements'     => !empty($_POST['prli_disable_url_replacements']),
            'hide_social_buttons'          => !empty($_POST['prli_hide_social_buttons']),
            'requested_slug'               => isset($_POST['prli_requested_slug'])
                ? sanitize_text_field((string) $_POST['prli_requested_slug'])
                : '',
        ]);
    }

    /**
     * Post types the meta box appears on: always post + page, plus any CPT
     * configured for auto-create, keyword replacement, or URL replacement.
     *
     * @return string[]
     */
    private function postTypes(): array
    {
        $types = ['post', 'page'];

        // `autocreate` is the unified per-CPT map (slug => row). A CPT is
        // relevant to the meta box once it has any row — the row itself
        // signals the admin configured something for it.
        $autocreate = $this->options->getPro('autocreate', []);
        if (is_array($autocreate)) {
            $types = array_merge($types, array_keys($autocreate));
        }

        foreach (['keyword_replacement_cpts', 'url_replacement_cpts'] as $opt) {
            $extra = $this->options->getPro($opt, []);
            if (is_array($extra)) {
                $types = array_merge($types, $extra);
            }
        }

        return array_values(array_unique(array_filter(array_map('strval', $types))));
    }
}
