<?php
/**
 * BookiFlex Core
 *
 * @package    BookiFlex
 * @subpackage Core
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */

declare(strict_types=1);

namespace BookiFlex\Core\Admin\CPT;

use BookiFlex\Core\Contract\PluginInterface;
use BookiFlex\Core\Util\Str;

defined( 'ABSPATH' ) || exit;

abstract class AbstractCPT
{
    public const POST_TYPE = '';

    protected PluginInterface $plugin;

    abstract public function registerPostType(): void;
    abstract public function registerMetaBoxes(): void;

    public function register(PluginInterface $plugin): void
    {
        $this->plugin = $plugin;
        $this->plugin->addAction('init', [$this, 'registerPostType'], 5);
        $this->plugin->addAction('carbon_fields_register_fields', [$this, 'registerMetaBoxes'], 5);
    }

    public function enableMenuOrderSorting($query): void
    {
        if (is_admin() && $query->is_main_query() && $query->get('post_type') === static::POST_TYPE) {
            $query->set('orderby', 'menu_order');
            $query->set('order', 'ASC');
        }
    }

    protected function customizeMenuOrder(string $title, string $description): void
    {
        $this->plugin->addAction('add_meta_boxes', function () use ($title, $description) {
            add_meta_box(
                'bflex_' . static::POST_TYPE . '_menu_order',
                $title,
                function ($post) use ($description) {
                    echo '<input name="menu_order" type="number" min="0" id="menu_order" value="' . esc_attr($post->menu_order) . '" style="width:100%" />';
                    echo '<p class="description">' . esc_html($description) . '</p>';
                },
                static::POST_TYPE,
                'side'
            );
        });
    }

    protected function customizeParentSelector(string $title, string $description): void
    {
        $this->plugin->addAction('add_meta_boxes', function () use ($title, $description) {
            add_meta_box(
                'bflex_' . static::POST_TYPE . '_parent',
                $title,
                function ($post) use ($description) {
                    $dropdown = wp_dropdown_pages([
                        'post_type'        => $post->post_type,
                        'exclude_tree'     => $post->ID,
                        'selected'         => $post->post_parent,
                        'name'             => 'parent_id',
                        'show_option_none' => __('(no parent)', 'bookiflex'),
                        'sort_column'      => 'menu_order, post_title',
                        'echo'             => 0,
                    ]);
                    if ($dropdown) {
                        echo $dropdown;
                    }
                    echo '<p class="description">' . esc_html($description) . '</p>';
                },
                static::POST_TYPE,
                'side'
            );
        });
    }

    protected function customizeExcerpt(string $title, string $description): void
    {
        $this->plugin->addAction('add_meta_boxes', function () use ($title, $description) {
            add_meta_box(
                'bflex_' . static::POST_TYPE . '_excerpt',
                $title,
                function ($post) use ($title, $description) {
                    ?>
                    <label class="screen-reader-text" for="excerpt"><?php echo esc_html($title); ?></label>
                    <textarea rows="1" cols="40" name="excerpt" id="excerpt"><?php echo esc_html($post->post_excerpt); ?></textarea>
                    <p><?php echo esc_html($description); ?></p>
                    <?php
                },
                static::POST_TYPE,
                'normal'
            );
        });
    }

    /**
     * Register auto-generation of short code field for this CPT
     *
     * Automatically generates a short code (3-5 characters) from post title
     * if the field is empty when saving the post.
     *
     * Uses carbon_fields_post_meta_container_saved hook to run AFTER Carbon Fields
     * has saved all its data, ensuring our generated value doesn't get overwritten.
     *
     * @param string $fieldName Carbon Fields meta field name (e.g., 'bflex__at__short_code')
     */
    protected function registerShortCodeAutoGeneration(string $fieldName): void
    {
        $this->plugin->addAction('carbon_fields_post_meta_container_saved', [$this, 'handleShortCodeGeneration'], 10, 1);

        // Store field name for use in handler
        $this->shortCodeFieldName = $fieldName;
    }

    /**
     * Handle short code generation after Carbon Fields saves post meta
     */
    public function handleShortCodeGeneration(int $postId): void
    {
        $fieldName = $this->shortCodeFieldName ?? null;

        if (!$fieldName) {
            return;
        }

        // Check if this is the correct post type
        if (get_post_type($postId) !== static::POST_TYPE) {
            return;
        }

        // Get current short code value
        $currentCode = carbon_get_post_meta($postId, $fieldName);

        // If code already exists, don't regenerate
        if (!empty($currentCode)) {
            return;
        }

        // Get post for title
        $post = get_post($postId);
        if (!$post || empty($post->post_title)) {
            return;
        }

        // Generate base code from post title (without uniqueness check)
        $baseCode = Str::generateCode($post->post_title, []);

        // Ensure uniqueness by checking database and adding numeric suffix if needed
        $code = $baseCode;
        $counter = 2;

        while ($this->isShortCodeExists($code, $fieldName, $postId) && $counter < 1000) {
            $suffix = (string)$counter;
            $maxBaseLength = 5 - strlen($suffix);
            $code = substr($baseCode, 0, $maxBaseLength) . $suffix;
            $counter++;
        }

        // Save the generated code
        carbon_set_post_meta($postId, $fieldName, $code);
    }

    /**
     * Stored field name for short code generation
     */
    protected ?string $shortCodeFieldName = null;

    /**
     * Check if a short code already exists for this post type
     *
     * Uses optimized meta_query to check specific code existence
     * instead of fetching all posts.
     *
     * @param string $code Short code to check
     * @param string $fieldName Carbon Fields meta field name
     * @param int|null $excludePostId Post ID to exclude from check (for updates)
     * @return bool True if code exists, false otherwise
     */
    private function isShortCodeExists(string $code, string $fieldName, ?int $excludePostId = null): bool
    {
        $args = [
            'post_type'      => static::POST_TYPE,
            'posts_per_page' => 1,
            'post_status'    => 'any',
            'fields'         => 'ids',
            'meta_query'     => [
                [
                    'key'     => '_' . $fieldName, // Carbon Fields prefixes with underscore
                    'value'   => $code,
                    'compare' => '=',
                ]
            ]
        ];

        // Exclude current post when checking for updates
        if ($excludePostId !== null) {
            $args['post__not_in'] = [$excludePostId];
        }

        $query = new \WP_Query($args);

        return $query->found_posts > 0;
    }
}
