<?php

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedNamespaceFound -- APD is the established namespace shared with the Pro add-on and retained for backward compatibility.
namespace APD\Api\Middleware;

use APD\Support\Capabilities;
use WP_Error;
use WP_REST_Request;

defined('ABSPATH') || exit;

class PermissionGuard
{
    public static function public() : bool
    {
        return true;
    }

    public static function admin() : bool
    {
        return current_user_can('manage_options');
    }

    public static function runtime(WP_REST_Request $request) : bool|WP_Error
    {
        if (NonceGuard::verify($request)) {
            return true;
        }

        return new WP_Error(
            'apd_rest_forbidden',
            __('The request could not be verified.', 'asuka-product-designer'),
            ['status' => 403]
        );
    }

    public static function authenticatedRuntime(WP_REST_Request $request) : bool|WP_Error
    {
        $verified = self::runtime($request);
        if (is_wp_error($verified)) {
            return $verified;
        }

        if (is_user_logged_in()) {
            return true;
        }

        return new WP_Error(
            'apd_rest_authentication_required',
            __('You must be logged in to access this resource.', 'asuka-product-designer'),
            ['status' => 401]
        );
    }

    public static function storefrontConfig(WP_REST_Request $request) : bool|WP_Error
    {
        $verified = self::runtime($request);
        if (is_wp_error($verified)) {
            return $verified;
        }

        if (self::canManageDesigner()) {
            return true;
        }

        $configId = absint($request['id']);
        if (! self::isPublishedConfig($configId) || ! self::configHasPublicProductBinding($configId)) {
            return new WP_Error(
                'apd_rest_config_forbidden',
                __('This product configuration is not publicly available.', 'asuka-product-designer'),
                ['status' => 403]
            );
        }

        return true;
    }

    public static function storefrontProduct(WP_REST_Request $request) : bool|WP_Error
    {
        $verified = self::runtime($request);
        if (is_wp_error($verified)) {
            return $verified;
        }

        if (self::canManageDesigner()) {
            return true;
        }

        $productId = absint($request['id']);
        if (! self::isPublicProduct($productId)) {
            return new WP_Error(
                'apd_rest_product_forbidden',
                __('This product is not publicly available.', 'asuka-product-designer'),
                ['status' => 403]
            );
        }

        $variationId = absint($request->get_param('variation_id'));
        if ($variationId > 0) {
            $variation = get_post($variationId);
            if (
                ! $variation
                || $variation->post_type !== 'product_variation'
                || $variation->post_status !== 'publish'
                || (int) $variation->post_parent !== $productId
            ) {
                return new WP_Error(
                    'apd_rest_variation_forbidden',
                    __('This product variation is not publicly available.', 'asuka-product-designer'),
                    ['status' => 403]
                );
            }
        }

        return true;
    }

    public static function storefrontPricing(WP_REST_Request $request) : bool|WP_Error
    {
        $verified = self::runtime($request);
        if (is_wp_error($verified)) {
            return $verified;
        }

        if (self::canManageDesigner()) {
            return true;
        }

        $payload = $request->get_json_params();
        $configId = absint(is_array($payload) ? ($payload['configId'] ?? 0) : 0);
        if ($configId <= 0) {
            return true;
        }

        if (! self::isPublishedConfig($configId) || ! self::configHasPublicProductBinding($configId)) {
            return new WP_Error(
                'apd_rest_pricing_forbidden',
                __('Pricing is not publicly available for this product configuration.', 'asuka-product-designer'),
                ['status' => 403]
            );
        }

        return true;
    }

    public static function manage(WP_REST_Request $request) : bool|WP_Error
    {
        $verified = self::runtime($request);
        if (is_wp_error($verified)) {
            return $verified;
        }

        if (current_user_can(Capabilities::MANAGE) || current_user_can('manage_options')) {
            return true;
        }

        return new WP_Error(
            'apd_rest_cap_forbidden',
            __('You are not allowed to access this resource.', 'asuka-product-designer'),
            ['status' => 403]
        );
    }

    private static function canManageDesigner() : bool
    {
        return current_user_can(Capabilities::MANAGE) || current_user_can('manage_options');
    }

    private static function isPublishedConfig(int $configId) : bool
    {
        $config = $configId > 0 ? get_post($configId) : null;

        return $config
            && $config->post_type === 'apd_product_config'
            && $config->post_status === 'publish';
    }

    private static function isPublicProduct(int $productId) : bool
    {
        $product = $productId > 0 ? get_post($productId) : null;
        if (! $product || $product->post_type !== 'product' || $product->post_status !== 'publish') {
            return false;
        }

        return is_post_publicly_viewable($product) && ! post_password_required($product);
    }

    private static function configHasPublicProductBinding(int $configId) : bool
    {
        $productIds = get_posts([
            'post_type' => 'product',
            'post_status' => 'publish',
            'posts_per_page' => 1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'meta_key' => '_apd_custom_product_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Storefront authorization resolves the plugin's explicit product binding.
            'meta_value' => $configId, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- The bounded query checks one public binding only.
        ]);
        foreach ($productIds as $productId) {
            if (self::isPublicProduct((int) $productId)) {
                return true;
            }
        }

        $variationIds = get_posts([
            'post_type' => 'product_variation',
            'post_status' => 'publish',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'meta_key' => '_apd_custom_product_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Storefront authorization resolves the plugin's explicit variation binding.
            'meta_value' => $configId, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Only matching variation identifiers are returned.
        ]);
        foreach ($variationIds as $variationId) {
            $variation = get_post((int) $variationId);
            if ($variation && self::isPublicProduct((int) $variation->post_parent)) {
                return true;
            }
        }

        return false;
    }
}
