<?php
namespace BLR\Backline\Core;

if (!defined('ABSPATH')) {
    exit;
}

final class RelatedRecords {
    private const DEFAULT_OPTION_LIMIT = 200;

    private static function can_include_products(bool $include_woocommerce): bool {
        return $include_woocommerce
            && class_exists('WooCommerce')
            && post_type_exists('product')
            && current_user_can('edit_products');
    }

    public static function supported_post_types(bool $include_woocommerce = true): array {
        $types = [
            'blr_release' => __('Album', 'blr-backline'),
            'blr_track' => __('Track', 'blr-backline'),
            'blr_event' => __('Event', 'blr-backline'),
            'blr_contact' => __('Contact', 'blr-backline'),
            'blr_outreach' => __('Outreach', 'blr-backline'),
            'blr_task' => __('Task', 'blr-backline'),
        ];

        if (self::can_include_products($include_woocommerce)) {
            $types['product'] = __('Woo Product', 'blr-backline');
        }

        return $types;
    }

    public static function option_label($post): string {
        $post = get_post($post);
        if (!$post instanceof \WP_Post) {
            return __('Unknown record', 'blr-backline');
        }

        if ($post->post_type === 'product' && !self::can_include_products(true)) {
            return '';
        }

        $labels = self::supported_post_types();
        $type_label = $labels[$post->post_type] ?? ucfirst(str_replace(['blr_', '_'], ['', ' '], $post->post_type));
        $title = get_the_title($post);
        if ($title === '') {
            $title = '#' . (int) $post->ID;
        }

        if ($post->post_type === 'product') {
            $sku = (string) get_post_meta($post->ID, '_sku', true);
            if ($sku !== '') {
                /* translators: %s: SKU value. */
                $title .= sprintf(__(' — SKU %s', 'blr-backline'), $sku);
            }
        }

        /* translators: 1: Related record type label. 2: Record title. */
        return sprintf(__('%1$s: %2$s', 'blr-backline'), $type_label, $title);
    }

    public static function options(array $args = []): array {
        $include_woocommerce = !empty($args['include_woocommerce']);
        $exclude_post_types = array_filter(array_map('sanitize_key', (array) ($args['exclude_post_types'] ?? [])));
        $include_post_ids = array_values(array_unique(array_filter(array_map('absint', (array) ($args['include_post_ids'] ?? [])))));
        $per_type_limit = isset($args['per_type_limit']) ? absint($args['per_type_limit']) : self::DEFAULT_OPTION_LIMIT;
        if ($per_type_limit < 1) {
            $per_type_limit = self::DEFAULT_OPTION_LIMIT;
        }

        /**
         * Filters the maximum number of related-record options loaded per post type.
         *
         * @param int   $per_type_limit Maximum posts loaded per post type.
         * @param array $args           Options arguments.
         */
        $per_type_limit = (int) apply_filters('blr_backline_related_records_per_type_limit', $per_type_limit, $args);
        if ($per_type_limit < 1) {
            $per_type_limit = self::DEFAULT_OPTION_LIMIT;
        }

        $types = array_diff(array_keys(self::supported_post_types($include_woocommerce)), $exclude_post_types);

        $options = [];
        foreach ($types as $post_type) {
            if (!post_type_exists($post_type)) {
                continue;
            }

            $posts = get_posts([
                'post_type' => $post_type,
                'post_status' => ['publish', 'draft', 'private'],
                'numberposts' => $per_type_limit,
                'orderby' => 'modified',
                'order' => 'DESC',
                'no_found_rows' => true,
                'suppress_filters' => false,
            ]);

            foreach ($posts as $post) {
                $label = self::option_label($post);
                if ($label === '') {
                    continue;
                }

                $options[(int) $post->ID] = $label;
            }
        }

        foreach ($include_post_ids as $post_id) {
            if ($post_id > 0 && !isset($options[$post_id])) {
                $descriptor = self::descriptor($post_id);
                if (!empty($descriptor['id']) && $descriptor['label'] !== '') {
                    $options[(int) $descriptor['id']] = $descriptor['label'];
                }
            }
        }

        return $options;
    }

    public static function descriptor(int $post_id): array {
        $post = get_post($post_id);
        if (!$post instanceof \WP_Post) {
            return [
                'id' => 0,
                'post_type' => '',
                'label' => '',
                'title' => '',
            ];
        }

        return [
            'id' => (int) $post->ID,
            'post_type' => (string) $post->post_type,
            'label' => self::option_label($post),
            'title' => (string) get_the_title($post),
        ];
    }
}
