<?php

namespace SoftminalB2B\Http\Controllers;

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

use SoftminalB2B\Cart\BulkAdder;
use SoftminalB2B\Models\PurchaseList;

/**
 * Saved purchase lists (FEATURES F1 — reorder).
 *
 * A buyer curates quantities in the bulk order form, saves them as a named
 * list, and later reorders the whole thing in one click. Reorder re-prices live
 * through the same validated cart path as "Add all to cart" (see BulkAdder), so
 * a saved list never carries a stale price.
 *
 * The route group is `can:read` (any logged-in customer); every list is owned by
 * a single user, so each method re-checks ownership against the current user and
 * treats someone else's list as not-found (never leak that it exists).
 */
class PurchaseListsController extends Controller
{
    private const MAX_LISTS = 100;

    /**
     * All of the current buyer's lists, newest first, plus lists colleagues
     * have shared with the buyer's company (Pro companies). Shared lists carry
     * the owner's name and are read-only for everyone but their owner.
     */
    public function index(\WP_REST_Request $request, $params = [])
    {
        $userId = get_current_user_id();

        // get() returns a Collection — flatten to arrays before merging or
        // mapping (the Ledger once shipped this exact type confusion).
        $lists = iterator_to_array(PurchaseList::where('user_id', $userId)
            ->orderBy('updated_at', 'DESC')
            ->get());

        $companyId = $this->companyId($userId);

        if ($companyId !== null) {
            $shared = iterator_to_array(PurchaseList::where('company_id', $companyId)
                ->andWhere('user_id', '!=', $userId)
                ->orderBy('updated_at', 'DESC')
                ->get());

            $lists = array_merge($lists, $shared);

            // Keep the documented "newest first" across BOTH sources — the
            // merge is two independently-sorted blocks otherwise.
            usort($lists, static function ($a, $b) {
                return strcmp((string) $b->updated_at, (string) $a->updated_at);
            });
        }

        return $this->success([
            'lists'     => array_map([$this, 'present'], $lists),
            // The client shows the share toggle only when sharing can work.
            'can_share' => $companyId !== null,
        ]);
    }

    /**
     * Share a list with the owner's company, or make it personal again.
     * Owner-only; needs a company membership (a Pro concept resolved through
     * the same seam pricing uses, so core carries no Pro dependency).
     */
    public function share(\WP_REST_Request $request, $params = [])
    {
        $list = $this->ownedOrNull($request);

        if (!$list) {
            return $this->notFound();
        }

        $shared = filter_var($request->get_param('shared'), FILTER_VALIDATE_BOOLEAN);

        if ($shared) {
            $companyId = $this->companyId(get_current_user_id());

            if ($companyId === null) {
                return $this->error(__('Sharing needs a company account.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
            }

            $list->company_id = $companyId;
        } else {
            $list->company_id = null;
        }

        $list->save();

        return $this->success(
            ['list' => $this->present($list)],
            $shared
                ? __('Shared with your company.', 'softminal-b2b-wholesale-for-woocommerce')
                : __('This list is personal again.', 'softminal-b2b-wholesale-for-woocommerce')
        );
    }

    /**
     * Save the current selection as a new named list.
     */
    public function store(\WP_REST_Request $request, $params = [])
    {
        $name  = $this->cleanName($request->get_param('name'));
        $items = PurchaseList::normalizeItems($request->get_param('items'));

        if ($name === '') {
            return $this->error(__('Give your list a name.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
        }

        if (empty($items)) {
            return $this->error(__('Add a quantity to at least one product before saving.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
        }

        $userId = get_current_user_id();

        if (PurchaseList::where('user_id', $userId)->count() >= self::MAX_LISTS) {
            return $this->error(__('You have reached the maximum number of saved lists.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
        }

        $list = PurchaseList::create([
            'user_id' => $userId,
            'name'    => $name,
            'items'   => $items,
        ]);

        return $this->success(['list' => $this->present($list)], __('List saved.', 'softminal-b2b-wholesale-for-woocommerce'));
    }

    /**
     * Rename a list and/or replace its lines.
     */
    public function update(\WP_REST_Request $request, $params = [])
    {
        $list = $this->ownedOrNull($request);

        if (!$list) {
            return $this->notFound();
        }

        if ($request->get_param('name') !== null) {
            $name = $this->cleanName($request->get_param('name'));

            if ($name === '') {
                return $this->error(__('Give your list a name.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
            }

            $list->name = $name;
        }

        if ($request->get_param('items') !== null) {
            $items = PurchaseList::normalizeItems($request->get_param('items'));

            if (empty($items)) {
                return $this->error(__('A list needs at least one product.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
            }

            $list->items = $items;
        }

        $list->save();

        return $this->success(['list' => $this->present($list)], __('List updated.', 'softminal-b2b-wholesale-for-woocommerce'));
    }

    /**
     * Delete a list.
     */
    public function destroy(\WP_REST_Request $request, $params = [])
    {
        $list = $this->ownedOrNull($request);

        if (!$list) {
            return $this->notFound();
        }

        $list->delete();

        return $this->success([], __('List deleted.', 'softminal-b2b-wholesale-for-woocommerce'));
    }

    /**
     * Reorder: add every line to the cart at today's prices, reporting any that
     * could not be added (out of stock, changed rules) per line.
     */
    public function reorder(\WP_REST_Request $request, $params = [])
    {
        if (!function_exists('wc_load_cart')) {
            return $this->error(__('The cart is unavailable right now.', 'softminal-b2b-wholesale-for-woocommerce'), 503);
        }

        // Reorder is allowed from your own lists AND lists shared with your
        // company; editing stays owner-only.
        $list = $this->accessibleOrNull($request);

        if (!$list) {
            return $this->notFound();
        }

        $result  = BulkAdder::add(is_array($list->items) ? $list->items : []);
        $added   = $result['added'];
        $skipped = $result['skipped'];

        if ($added === 0) {
            return $this->response([
                'success' => false,
                'message' => __('Nothing from this list could be added — the products may be unavailable.', 'softminal-b2b-wholesale-for-woocommerce'),
                'data'    => ['added' => 0, 'skipped' => $skipped, 'cart_count' => WC()->cart->get_cart_contents_count()],
            ], 422);
        }

        return $this->success(
            [
                'added'      => $added,
                'skipped'    => $skipped,
                'cart_count' => WC()->cart->get_cart_contents_count(),
                'cart_total' => (float) WC()->cart->get_cart_contents_total(),
                'cart_url'   => wc_get_cart_url(),
            ],
            sprintf(
                /* translators: %d: number of products added */
                _n('%d product added to your cart.', '%d products added to your cart.', $added, 'softminal-b2b-wholesale-for-woocommerce'),
                $added
            )
        );
    }

    /**
     * Load a list by the {id} route param only if it belongs to the current
     * user; otherwise null (caller returns 404, so ownership is not leaked).
     */
    private function ownedOrNull(\WP_REST_Request $request): ?PurchaseList
    {
        $id   = absint($request->get_param('id'));
        $list = $id ? PurchaseList::find($id) : null;

        if (!$list || (int) $list->user_id !== get_current_user_id()) {
            return null;
        }

        return $list;
    }

    /**
     * A list the current user may USE: their own, or one shared with their
     * company. Mutations must keep using ownedOrNull().
     */
    private function accessibleOrNull(\WP_REST_Request $request): ?PurchaseList
    {
        $id   = absint($request->get_param('id'));
        $list = $id ? PurchaseList::find($id) : null;

        if (!$list) {
            return null;
        }

        $userId = get_current_user_id();

        if ((int) $list->user_id === $userId) {
            return $list;
        }

        $companyId = $this->companyId($userId);

        if ($companyId !== null && (int) $list->company_id === $companyId) {
            return $list;
        }

        return null;
    }

    /**
     * The user's company id via the same seam pricing uses (Pro answers it;
     * null on free or for buyers outside a company).
     */
    private function companyId(int $userId): ?int
    {
        $membership = apply_filters('smb2b_pricing_context_company', null, $userId);

        return is_array($membership) && !empty($membership['company_id'])
            ? (int) $membership['company_id']
            : null;
    }

    private function cleanName($raw): string
    {
        // Trim, strip tags, and cap at the column width (191).
        $name = trim(sanitize_text_field((string) $raw));

        return function_exists('mb_substr') ? mb_substr($name, 0, 191) : substr($name, 0, 191);
    }

    /**
     * @return array<string, mixed>
     */
    private function present(PurchaseList $list): array
    {
        $items = is_array($list->items) ? $list->items : [];
        $mine  = (int) $list->user_id === get_current_user_id();
        $owner = '';

        if (!$mine) {
            $user  = get_userdata((int) $list->user_id);
            $owner = $user ? $user->display_name : '';
        }

        return [
            'id'         => (int) $list->id,
            'name'       => $list->name,
            'items'      => $items,
            'item_count' => count($items),
            'updated_at' => $list->updated_at,
            'shared'     => $list->company_id !== null,
            'mine'       => $mine,
            'owner'      => $owner,
        ];
    }
}
