<?php

namespace SoftminalB2B\Http\Controllers;

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

use SoftminalB2B\Models\Group;
use SoftminalB2B\Pricing\Context;
use SoftminalB2B\Pricing\ResolvedPrice;
use SoftminalB2B\Pricing\Resolver;
use SoftminalB2B\Storefront\ViewAsCustomer;

/**
 * The A7 Price Explainer, and the `/price` route from IMPLEMENTATION §2.
 *
 * The explanation is read straight off the resolver's walk, so it cannot drift
 * from the price a shopper is actually charged.
 */
class PricingController extends Controller
{
    public function explain(\WP_REST_Request $request, $params = [])
    {
        $productId   = absint($request->get_param('product_id'));
        $variationId = absint($request->get_param('variation_id')) ?: null;
        $userId      = absint($request->get_param('user_id'));
        $groupId     = $request->get_param('group_id');
        $qty         = max(1, absint($request->get_param('qty')) ?: 1);

        if ($productId <= 0 || !wc_get_product($productId)) {
            return $this->error(__('Pick a product to check.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
        }

        // The merchant can ask about a specific customer, or about a group in
        // the abstract ("what would any wholesale buyer pay?").
        $previewUserId = $userId;

        if (!$previewUserId && $groupId !== null && $groupId !== '') {
            $previewUserId = $this->representativeMember((int) $groupId);

            if ($previewUserId === null) {
                return $this->explainForGroupWithoutMembers((int) $groupId, $productId, $variationId, $qty);
            }
        }

        Resolver::flushRequestMemo();
        Context::flush();

        $resolved = Resolver::resolve($productId, $variationId, $previewUserId, $qty);

        return $this->success($this->presentExplanation($resolved, $productId, $variationId, $previewUserId, $qty));
    }

    /**
     * A brand-new group has no customers yet, but the merchant still needs to
     * see what its pricing will do. Resolve against a throwaway context by
     * temporarily assigning the current admin, then put them back.
     */
    private function explainForGroupWithoutMembers(int $groupId, int $productId, ?int $variationId, int $qty)
    {
        if (!Group::find($groupId)) {
            return $this->notFound(__('That customer group no longer exists.', 'softminal-b2b-wholesale-for-woocommerce'));
        }

        $adminId  = get_current_user_id();
        $previous = get_user_meta($adminId, Group::USER_META_KEY, true);

        Group::assignUser($adminId, $groupId);
        Resolver::flushRequestMemo();
        Context::flush();

        try {
            $resolved = Resolver::resolve($productId, $variationId, $adminId, $qty);
            $payload  = $this->presentExplanation($resolved, $productId, $variationId, $adminId, $qty);
        } finally {
            if ($previous === '' || $previous === false) {
                Group::assignUser($adminId, null);
            } else {
                Group::assignUser($adminId, (int) $previous);
            }

            Resolver::flushRequestMemo();
            Context::flush();
        }

        $payload['buyer']['name'] = __('Any customer in this group', 'softminal-b2b-wholesale-for-woocommerce');
        $payload['notice'] = __('This group has no customers yet, so we previewed it with a sample buyer.', 'softminal-b2b-wholesale-for-woocommerce');

        return $this->success($payload);
    }

    /**
     * @return array<string, mixed>
     */
    private function presentExplanation(ResolvedPrice $resolved, int $productId, ?int $variationId, int $userId, int $qty): array
    {
        $product = wc_get_product($variationId ?: $productId);
        $context = Context::forUser($userId);
        $group   = $context->groupId() ? Group::find($context->groupId()) : null;
        $user    = $userId ? get_userdata($userId) : null;

        return [
            'product' => [
                'id'    => $productId,
                'name'  => $product ? $product->get_name() : '',
                'variation_id' => $variationId,
            ],
            'buyer' => [
                'id'         => $userId,
                'name'       => $user ? $user->display_name : __('A guest (not logged in)', 'softminal-b2b-wholesale-for-woocommerce'),
                'group_id'   => $context->groupId(),
                'group_name' => $group ? $group->name : __('No group (retail pricing)', 'softminal-b2b-wholesale-for-woocommerce'),
            ],
            'qty'     => $qty,
            'price'   => $resolved->price(),
            'price_formatted' => $this->formatPrice($resolved->price()),
            'base_price' => $resolved->basePrice(),
            'base_price_formatted' => $this->formatPrice($resolved->basePrice()),
            'is_discounted' => $resolved->isDiscounted(),
            'savings' => $resolved->savings(),
            'savings_formatted' => $this->formatPrice($resolved->savings()),
            'savings_percent' => $resolved->savingsPercent(),
            'source'  => $resolved->source(),
            'source_label' => $this->sourceLabel($resolved->source()),
            'rule_id' => $resolved->ruleId(),
            'steps'   => $this->presentTrail($resolved->trail()),
            'summary' => $resolved->explain(),
            'view_as_url' => ViewAsCustomer::previewUrl($userId),
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $trail
     * @return array<int, array<string, mixed>>
     */
    private function presentTrail(array $trail): array
    {
        $steps = [];

        foreach ($trail as $step) {
            $steps[] = [
                'level'       => $step['level'],
                'level_label' => $this->sourceLabel($step['level']),
                'outcome'     => $step['outcome'],
                'detail'      => $step['detail'],
                'rule_id'     => $step['rule_id'],
            ];
        }

        return $steps;
    }

    /**
     * Plain language only — the UX Doctrine bans "precedence" and "resolution"
     * from anything a merchant reads.
     */
    private function sourceLabel(string $source): string
    {
        switch ($source) {
            case ResolvedPrice::SOURCE_QUOTE:
                return __('An agreed quote price', 'softminal-b2b-wholesale-for-woocommerce');
            case ResolvedPrice::SOURCE_CUSTOMER:
                return __('A price set for this one customer', 'softminal-b2b-wholesale-for-woocommerce');
            case ResolvedPrice::SOURCE_COMPANY:
                return __('A price list for their company', 'softminal-b2b-wholesale-for-woocommerce');
            case ResolvedPrice::SOURCE_GROUP_PRODUCT:
                return __('A price for this product and group', 'softminal-b2b-wholesale-for-woocommerce');
            case ResolvedPrice::SOURCE_GROUP_TAXONOMY:
                return __('A price for this category or tag', 'softminal-b2b-wholesale-for-woocommerce');
            case ResolvedPrice::SOURCE_GROUP_GLOBAL:
                return __('The group\'s storewide discount', 'softminal-b2b-wholesale-for-woocommerce');
        }

        return __('The normal shop price', 'softminal-b2b-wholesale-for-woocommerce');
    }

    private function representativeMember(int $groupId): ?int
    {
        $query = new \WP_User_Query([
            'meta_key'   => Group::USER_META_KEY, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
            'meta_value' => (string) $groupId, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
            'fields'     => 'ID',
            'number'     => 1,
        ]);

        $results = $query->get_results();

        return empty($results) ? null : (int) $results[0];
    }

    private function formatPrice(float $price): string
    {
        return html_entity_decode(wp_strip_all_tags(wc_price($price)));
    }
}
