<?php
/**
 * Upsells
 *
 * Manual per-product upsell associations, surfaced in the assessment
 * "Complete your basket" overlay. Cloned from ComplimentedItems but
 * manual-config-only (no auto-supplement / member-specific logic).
 */

namespace Mtc\Plugins\Upsells\Classes;

use Illuminate\Database\Eloquent\Model;
use Mtc\Shop\Item as ShopItem;
use Twig\Environment;

class Upsells extends Model
{
    /**
     * @var string Classes associated table
     */
    protected $table = 'items_upsells';

    /**
     * @var string[] Fillable fields when using ->fill()
     */
    protected $fillable = [
        'item_id',
        'upsell_item_id',
        'order',
    ];

    /**
     * Add the "Upsells" tab to the product admin tabnav.
     *
     * Listens to Mtc\Shop\Events\ItemAdminTabnavEvent; the returned HTML is
     * echoed into the tab strip by shop/admin/products/tabnav.php.
     *
     * @param \Item $item Currently opened item (legacy item POD)
     * @param array $request $_REQUEST
     * @param Environment $twig Twig handler
     * @return string|false Tab HTML, or false when it should not render
     */
    public static function addTabNav(\Item $item, array $request, Environment $twig)
    {
        if (!config('upsells.enabled') || empty($request['id'])) {
            return false;
        }

        return $twig->render('Upsells/admin/tabnav.twig', [
            'item' => $item,
            'request' => $request,
            'request_uri' => $_SERVER['REQUEST_URI'],
        ]);
    }

    /**
     * Given a set of product IDs, return those that are assessment-required (and
     * therefore can never be upsells). Resolved in a single query. Shared by the
     * admin save step and the per-row "ineligible" warning.
     *
     * @param array $ids
     * @return int[]
     */
    public static function assessmentRequiredIds(array $ids): array
    {
        $ids = array_filter(array_map('intval', $ids));

        if (empty($ids)) {
            return [];
        }

        return ShopItem::query()
            ->with('categories')
            ->whereIn('id', $ids)
            ->get()
            ->filter(function ($item) {
                return $item->requiresAssessment();
            })
            ->pluck('id')
            ->map('intval')
            ->all();
    }

    /**
     * Resolve the ordered, eligible upsell products for a given product,
     * shaped for the frontend overlay.
     *
     * Thin wrapper over getUpsellsForCollection() for single-product callers.
     *
     * @param ShopItem $item The product the upsells belong to
     * @return array<int, array> Overlay-shaped upsell rows
     */
    public static function getUpsellsFor(ShopItem $item): array
    {
        return self::getUpsellsForCollection([$item])[$item->id] ?? [];
    }

    /**
     * Resolve eligible, overlay-shaped upsells for a whole set of products in a
     * fixed two queries (configs, then referenced products) — avoids the N+1 of
     * calling getUpsellsFor() per product in a loop.
     *
     * Live eligibility: assessment-required products are dropped here (no admin
     * change needed if a configured upsell later becomes assessment-required),
     * alongside hidden / deleted / out-of-stock products.
     *
     * @param iterable $items Products the upsells belong to
     * @return array<int, array<int, array>> Map of item_id => overlay-shaped upsell rows
     */
    public static function getUpsellsForCollection($items): array
    {
        if (!config('upsells.enabled')) {
            return [];
        }

        $item_ids = collect($items)->pluck('id')->map('intval')->unique()->values()->all();

        if (empty($item_ids)) {
            return [];
        }

        // One query: every configured upsell row for these items, in display order.
        $configs = self::query()
            ->whereIn('item_id', $item_ids)
            ->orderBy('order', 'ASC')
            ->get(['item_id', 'upsell_item_id']);

        if ($configs->isEmpty()) {
            return [];
        }

        // One query: resolve every referenced upsell product with its relations.
        $upsell_ids = $configs->pluck('upsell_item_id')->unique()->values()->all();
        $items_by_id = ShopItem::query()
            ->with(['sizes', 'images', 'categories'])
            ->whereIn('id', $upsell_ids)
            ->get()
            ->keyBy('id');

        $upsells_by_item = [];
        foreach ($configs as $config) {
            $upsell_item = $items_by_id->get($config->upsell_item_id);

            if (!$upsell_item) {
                continue;
            }

            // Live eligibility + availability gate (single source of truth).
            if ($upsell_item->requiresAssessment()
                || $upsell_item->isHidden()
                || $upsell_item->deleted
                || !$upsell_item->in_stock
            ) {
                continue;
            }

            $upsells_by_item[$config->item_id][] = self::shapeForOverlay($upsell_item);
        }

        return $upsells_by_item;
    }

    /**
     * Shape a product for an upsell overlay row.
     *
     * @param ShopItem $item
     * @return array
     */
    protected static function shapeForOverlay(ShopItem $item): array
    {
        $sizes = $item->sizes
            ->map(function ($size) {
                return [
                    'id' => $size->id,
                    'size' => $size->size,
                    'price' => (float) $size->price,
                    'sale_price' => (float) $size->sale_price,
                    'stock' => (int) $size->stock,
                    'in_stock' => (int) $size->stock > 0,
                ];
            })
            ->values()
            ->toArray();

        // Meta line under the name (e.g. size / pack) — default to the first size label.
        $meta = !empty($sizes) ? ($sizes[0]['size'] ?? '') : '';

        return [
            'id' => $item->id,
            'name' => $item->name,
            'image' => $item->image,
            'url' => $item->url,
            'in_stock' => (bool) $item->in_stock,
            'price' => (float) $item->price,
            'sale_price' => (float) $item->sale_price,
            'from_price' => (float) $item->from_price,
            'sizes' => $sizes,
            'meta' => $meta,
        ];
    }
}
