<?php

/**
 * My Account Menu Configuration REST Controller
 *
 * Admin-only endpoints for managing My Account menu layout.
 *
 * @package ArraySubs\Features\MyAccountEditor\REST
 * @since 1.9.0
 */

namespace ArraySubs\Features\MyAccountEditor\REST;

defined('ABSPATH') || exit;

use ArraySubs\Supports\BaseRestController;

/**
 * Menu configuration REST controller.
 */
class MenuConfigController extends BaseRestController
{
    /**
     * Constructor.
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Register REST routes.
     *
     * @return void
     */
    public function registerRoutes(): void
    {
        register_rest_route(
            $this->namespace,
            '/myaccount-editor/config',
            [
                [
                    'methods'             => 'GET',
                    'callback'            => [$this, 'getConfig'],
                    'permission_callback' => [$this, 'checkAdminPermission'],
                ],
                [
                    'methods'             => 'POST',
                    'callback'            => [$this, 'saveConfig'],
                    'permission_callback' => [$this, 'checkAdminPermission'],
                ],
            ]
        );

        register_rest_route(
            $this->namespace,
            '/myaccount-editor/defaults',
            [
                [
                    'methods'             => 'GET',
                    'callback'            => [$this, 'getDefaults'],
                    'permission_callback' => [$this, 'checkAdminPermission'],
                ],
            ]
        );

        register_rest_route(
            $this->namespace,
            '/myaccount-editor/search-content',
            [
                [
                    'methods'             => 'GET',
                    'callback'            => [$this, 'searchContent'],
                    'permission_callback' => [$this, 'checkAdminPermission'],
                    'args'                => [
                        'search' => [
                            'required'          => false,
                            'type'              => 'string',
                            'sanitize_callback' => 'sanitize_text_field',
                        ],
                        'page' => [
                            'required'          => false,
                            'type'              => 'integer',
                            'default'           => 1,
                            'sanitize_callback' => 'absint',
                        ],
                    ],
                ],
            ]
        );
    }

    /**
     * Check admin permission.
     *
     * @return bool
     */
    public function checkAdminPermission(): bool
    {
        if (!is_user_logged_in()) {
            return false;
        }

        return current_user_can('manage_woocommerce') || current_user_can('manage_options');
    }

    /**
     * Get saved menu configuration.
     *
     * @return \WP_REST_Response
     */
    public function getConfig(): \WP_REST_Response
    {
        return arraysubs_rest_response(
            [
                'items'              => $this->prepareItemsForResponse(arraysubs_get_myaccount_menu_config()),
                'menu_items_enabled' => arraysubs_is_myaccount_menu_items_enabled(),
            ],
            200,
            esc_html__('Configuration retrieved', 'arraysubs')
        );
    }

    /**
     * Save menu configuration.
     *
     * @param \WP_REST_Request $request REST request.
     * @return \WP_REST_Response
     */
    public function saveConfig(\WP_REST_Request $request): \WP_REST_Response
    {
        $params = $request->get_json_params();

        if (array_key_exists('menu_items_enabled', $params)) {
            $was_enabled = arraysubs_is_myaccount_menu_items_enabled();
            $is_enabled  = (bool) $params['menu_items_enabled'];

            arraysubs_update_setting('profile_builder.my_account_menu_items_enabled', $is_enabled);

            if ($was_enabled !== $is_enabled) {
                arraysubs_schedule_myaccount_editor_flush();
            }
        }

        if (isset($params['items']) && !is_array($params['items'])) {
            return arraysubs_rest_response(
                null,
                400,
                esc_html__('Invalid configuration data', 'arraysubs')
            );
        }

        if (isset($params['items']) && is_array($params['items'])) {
            $sanitized_items = arraysubs_sanitize_myaccount_menu_config($params['items']);
            $validation      = $this->validateItems($sanitized_items);

            if (!$validation['valid']) {
                return arraysubs_rest_response(
                    null,
                    400,
                    $validation['message']
                );
            }

            arraysubs_save_myaccount_menu_config($params['items']);
        }

        if (!array_key_exists('menu_items_enabled', $params) && !isset($params['items'])) {
            return arraysubs_rest_response(
                null,
                400,
                esc_html__('Invalid configuration data', 'arraysubs')
            );
        }

        return arraysubs_rest_response(
            [
                'items'              => $this->prepareItemsForResponse(arraysubs_get_myaccount_menu_config()),
                'menu_items_enabled' => arraysubs_is_myaccount_menu_items_enabled(),
            ],
            200,
            esc_html__('Configuration saved', 'arraysubs')
        );
    }

    /**
     * Get default WooCommerce My Account menu items.
     *
     * Fetches the current items from WooCommerce (including items added
     * by ArraySubs and other plugins) to populate the editor initially.
     *
     * @return \WP_REST_Response
     */
    public function getDefaults(): \WP_REST_Response
    {
        $items = arraysubs_get_default_myaccount_menu_items();

        return arraysubs_rest_response(
            $this->prepareItemsForResponse($items),
            200,
            esc_html__('Default items retrieved', 'arraysubs')
        );
    }

    /**
     * Search pages and CPTs for linking.
     *
     * @param \WP_REST_Request $request REST request.
     * @return \WP_REST_Response
     */
    public function searchContent(\WP_REST_Request $request): \WP_REST_Response
    {
        $search   = sanitize_text_field($request->get_param('search') ?? '');
        $page     = absint($request->get_param('page') ?? 1);
        $per_page = 20;

        $post_types = get_post_types(['public' => true], 'objects');
        $allowed_types = [];

        foreach ($post_types as $pt) {
            if (in_array($pt->name, ['attachment'], true)) {
                continue;
            }
            $allowed_types[] = $pt->name;
        }

        $args = [
            'post_type'      => $allowed_types,
            'post_status'    => 'publish',
            'posts_per_page' => $per_page,
            'paged'          => min($page, 5),
            'orderby'        => 'title',
            'order'          => 'ASC',
        ];

        if (!empty($search)) {
            $args['s'] = $search;
        }

        $query   = new \WP_Query($args);
        $results = [];

        foreach ($query->posts as $post) {
            $pt_object = get_post_type_object($post->post_type);
            $results[] = [
                'id'        => $post->ID,
                'title'     => $post->post_title ?: esc_html__('(No title)', 'arraysubs'),
                'post_type' => $post->post_type,
                'type_label' => $pt_object ? $pt_object->labels->singular_name : $post->post_type,
            ];
        }

        return arraysubs_rest_response(
            [
                'results'    => $results,
                'total'      => $query->found_posts,
                'total_pages' => min($query->max_num_pages, 5),
            ],
            200
        );
    }

    /**
     * Prepare menu items for frontend responses.
     *
     * Adds derived display data such as original labels for default items
     * and linked content titles for custom items.
     *
     * @param array $items Menu items.
     * @return array
     */
    private function prepareItemsForResponse(array $items): array
    {
        $default_labels = $this->getDefaultLabelMap();

        foreach ($items as $index => $item) {
            if (!is_array($item)) {
                continue;
            }

            $type = $item['type'] ?? 'default';

            if ('default' === $type) {
                $item['_original_label'] = $default_labels[$item['id'] ?? ''] ?? ($item['label'] ?? '');
            }

            if ('custom' === $type) {
                $content_id = absint($item['content_id'] ?? 0);

                if ($content_id > 0) {
                    $content_title = get_the_title($content_id);
                    $item['_content_title'] = !empty($content_title)
                        ? $content_title
                        : esc_html__('(No title)', 'arraysubs');
                }
            }

            $items[$index] = $item;
        }

        return $items;
    }

    /**
     * Validate sanitized menu items before saving.
     *
     * @param array $items Sanitized menu items.
     * @return array{valid: bool, message: string}
     */
    private function validateItems(array $items): array
    {
        $reserved_endpoints = array_keys($this->getDefaultLabelMap());
        $custom_endpoints   = [];

        foreach ($items as $item) {
            if (!is_array($item) || ('custom' !== ($item['type'] ?? 'default'))) {
                continue;
            }

            $endpoint   = sanitize_title($item['endpoint'] ?? '');
            $label      = sanitize_text_field($item['label'] ?? '');
            $content_id = absint($item['content_id'] ?? 0);

            if (empty($label) || empty($endpoint)) {
                return [
                    'valid'   => false,
                    'message' => esc_html__('All custom items must have a label and endpoint slug.', 'arraysubs'),
                ];
            }

            if ($content_id <= 0) {
                return [
                    'valid'   => false,
                    'message' => esc_html__('Each custom item must be linked to published content.', 'arraysubs'),
                ];
            }

            if (in_array($endpoint, $reserved_endpoints, true)) {
                return [
                    'valid'   => false,
                    'message' => sprintf(
                        /* translators: %s: endpoint slug */
                        esc_html__('The endpoint "%s" is already used by WooCommerce or another plugin.', 'arraysubs'),
                        $endpoint
                    ),
                ];
            }

            if (in_array($endpoint, $custom_endpoints, true)) {
                return [
                    'valid'   => false,
                    'message' => esc_html__('Custom endpoint slugs must be unique.', 'arraysubs'),
                ];
            }

            $custom_endpoints[] = $endpoint;
        }

        return [
            'valid'   => true,
            'message' => '',
        ];
    }

    /**
     * Get the current default My Account labels keyed by endpoint.
     *
     * @return array<string, string>
     */
    private function getDefaultLabelMap(): array
    {
        $defaults = arraysubs_get_default_myaccount_menu_items();
        $map      = [];

        foreach ($defaults as $item) {
            if (!is_array($item) || empty($item['id'])) {
                continue;
            }

            $map[$item['id']] = $item['label'] ?? '';
        }

        return $map;
    }
}
