<?php

namespace auctionforge\inc\widgets;

use auctionforge\inc\helpers\LotHelper;
use auctionforge\inc\helpers\SettingsHelper;
use auctionforge\inc\helpers\View;
use auctionforge\inc\model\Auction;
use auctionforge\inc\model\Lot;
use auctionforge\inc\model\LotMetaData;
use WP_Widget;

class LotsWidget extends WP_Widget {
    const MODE_DEFAULT = 'default';
    const MODE_ARCHIVE = 'archive';
    const MODE_UPCOMING = 'upcoming';
    const MODE_RANDOM_UPCOMING = 'random_upcoming';
    const MODE_RANDOM_PAST = 'random_past';
    const MODE_HIGH_VALUE_PAST = 'high_value_past';
    const MODE_HIGH_VALUE_SOLD = 'high_value_sold';

    private $lot_per_page_default = '5';
    private $mode = 'default';
    private $tmp = 'list';
    private $defaultArgs = array();

    function __construct() {
        parent::__construct(
            'BP_LotsWidget',
            'BP Lots',
            array( 'description' => 'BP Lots' )
        );
    }

    public function widget( $args, $instance ) {
        $title = apply_filters( 'widget_title', $instance['title'] );
        $lot_per_page = $instance['lot_per_page'];

        $lots = Lot::getLots($lot_per_page);

        echo View::render('templates.widgets.lots', array(
            'title' => $title,
            'lots' => $lots,
            'args' => $args
        ));
    }

    public function form( $instance ) {
        $title = '';
        $lot_auctions = '';
        $lot_per_page = $this->lot_per_page_default;

        if ( isset( $instance[ 'title' ] ) ) {
            $title = $instance[ 'title' ];
        }

        if ( isset( $instance[ 'lot_per_page' ] ) ) {
            $lot_per_page = $instance[ 'lot_per_page' ];
        }
        echo View::render('admin.view.widgets.lots', array(
            'obj' => $this,
            'title' => $title,
            'lot_auctions' => $lot_auctions,
            'lot_per_page' => $lot_per_page
        ));
    }

    public function update($new_instance, $old_instance)
    {
        $instance = array();
        $instance['title'] = (!empty($new_instance['title'])) ? strip_tags($new_instance['title']) : '';
        $instance['lot_auctions'] = (!empty($new_instance['lot_auctions'])) ? strip_tags($new_instance['lot_auctions']) : '';
        $instance['lot_per_page'] = (is_numeric($new_instance['lot_per_page'])) ? $new_instance['lot_per_page'] : $this->lot_per_page_default;
        return $instance;
    }

    public function lot_list_shortcode( $atts ) {
        /** @var $limit string */
        /** @var $title string */
        /** @var $mode string */
        /** @var $tmp string */
        /** @var $loadmore string */

        $atts = shortcode_atts( array(
            'title' => __('Lot List', 'auctionforge'),
            'limit' => $this->lot_per_page_default,
            'mode' => $this->mode,
            'tmp' => $this->tmp,
            'loadmore' => 1,
            'mobile_cols' => 2
        ), $atts );
        extract($atts);

        $randomModes = array(self::MODE_RANDOM_UPCOMING, self::MODE_RANDOM_PAST);
        $highValueModes = array(self::MODE_HIGH_VALUE_PAST, self::MODE_HIGH_VALUE_SOLD);
        $isDefault = !in_array($mode, array_merge(
            array(self::MODE_ARCHIVE, self::MODE_UPCOMING),
            $randomModes,
            $highValueModes
        ), true);
        $this->setDefaultArgs($limit);

        $lots = apply_filters('bp_lot_widgets_filter', null, $atts);

        if (is_null($lots)) {
            if (in_array($mode, $randomModes, true)) {
                $lots = $this->getLotListRandom($mode, $limit);
            } elseif (in_array($mode, $highValueModes, true)) {
                $lots = $this->getLotListHighValue($mode, $limit);
            } elseif ($isDefault) {
                $lots = $this->getLotListDefault();
            } else {
                $lots = $this->getLotList($mode);
            }

            if ($isDefault && empty($lots)) {
                $lots = Lot::getLots($limit);
            }
        }

        do_action('bp_lot_widgets_action', $lots);

        if ($atts['tmp'] == 'slider') {
            $template = 'widgets.slider';
        } else {
            $template = 'shortcodes.lots';
        }

        $showEstimatedPriceInItemsList = false;
        $showEstimatedPrice = SettingsHelper::isChecked('force_show_estimated_price');
        if ($showEstimatedPrice) {
            $showEstimatedPriceInItemsList = $showEstimatedPrice;
        }
        if (is_tax('auction_number') && is_archive()) {
            $auction = get_queried_object();
            $showEstimatedPriceInItemsList = SettingsHelper::getTermOption($auction->term_id, 'auction_showEstimatedPriceInItemsList', false);
            $displayStartPriceInCatalog = SettingsHelper::getTermOption($auction->term_id, 'auction_displayStartPriceInCatalog');
            $hiddenPrice = LotHelper::isHiddenLots($auctionDay);
        } else {
            $displayStartPriceInCatalog = true;
            $hiddenPrice = false;
        }

        return View::render('templates.' . $template, array(
            'title' => $title,
            'lots' => $lots,
            'limit' => $limit,
            'hiddenPrice' => $hiddenPrice,
            'showEstimatedPriceInItemsList' => $showEstimatedPriceInItemsList,
            'displayStartPriceInCatalog' => $displayStartPriceInCatalog,
            'loadmore' => intval($loadmore),
            'mobile_cols' => intval($atts['mobile_cols']),
            'random_mode' => in_array($mode, $randomModes, true),
        ));
    }

    public function getLotList($mode)
    {
        $args = $this->getDefaultArgs();

        if ($mode == self::MODE_ARCHIVE) {
            $auctions = Auction::getArchiveAuction();
        } else {
            $auctions = Auction::getActiveAuction();
        }

        $slugs = $this->pluckSlug($auctions);
        $args = $this->mergeArgs($args, $slugs);

        $args = apply_filters('bp_lot_list_mode_args_filter', $args, $mode);

        return get_posts($args);
    }

    public function getLotListDefault()
    {
        $args = $this->getDefaultArgs();
        $auctions = Auction::getActiveAuction();

        $slugs = $this->pluckSlug($auctions);
        $args = $this->mergeArgs($args, $slugs);

        $args = apply_filters('bp_lot_list_args_filter', $args);

        return get_posts($args);
    }

    public function getLotListRandom($mode, $limit)
    {
        if ($mode === self::MODE_RANDOM_PAST) {
            $auctions = Auction::getArchiveAuction();
        } else {
            $auctions = Auction::getActiveAuction();
        }

        $slugs = $this->pluckSlug($auctions);
        if (empty($slugs)) {
            return array();
        }

        $intLimit = intval($limit);

        $args = array(
            'numberposts'      => $intLimit,
            'post_status'      => array('publish'),
            'post_type'        => 'lot',
            'orderby'          => 'rand',
            'suppress_filters' => false,
            'tax_query'        => array(
                array(
                    'taxonomy' => 'auction_number',
                    'field'    => 'slug',
                    'terms'    => $slugs,
                ),
            ),
        );

        if ($mode === self::MODE_RANDOM_PAST) {
            $args[LotMetaData::BP_QUERY] = array(
                'bp_field' => array(
                    array(
                        'field'   => 'soldBid',
                        'value'   => 0,
                        'compare' => '>',
                    ),
                ),
            );

            // Over-fetch to account for false positives we'll filter out.
            // BidSpirit sometimes sets soldBid = startPrice on unsold lots
            // (where leadingBid = 0), so we need to remove those in PHP.
            $args['numberposts'] = $intLimit + 10;
        }

        $args = apply_filters('bp_lot_list_random_args_filter', $args, $mode);

        $posts = get_posts($args);

        // Filter out false-positive "sold" lots: soldBid equals startPrice
        // with no leading bid — these are unsold items with stale data.
        if ($mode === self::MODE_RANDOM_PAST && !empty($posts)) {
            $posts = array_filter($posts, function($lot) {
                $soldBid    = floatval(LotMetaData::getMetaField('soldBid', $lot->ID));
                $startPrice = floatval(LotMetaData::getMetaField('startPrice', $lot->ID));
                $leadingBid = floatval(LotMetaData::getMetaField('leadingBid', $lot->ID));

                // Keep the lot only if it's genuinely sold:
                // - soldBid differs from startPrice (bidding occurred), OR
                // - there was a leading bid (bidding activity confirmed)
                return ($soldBid != $startPrice) || ($leadingBid > 0);
            });

            $posts = array_values($posts);

            // Trim back to the requested limit
            if (count($posts) > $intLimit) {
                $posts = array_slice($posts, 0, $intLimit);
            }
        }

        return $posts;
    }

    public function getLotListHighValue($mode, $limit)
    {
        $auctions = Auction::getArchiveAuction();
        $slugs = $this->pluckSlug($auctions);
        if (empty($slugs)) {
            return array();
        }

        $intLimit = intval($limit);
        $isSold = ($mode === self::MODE_HIGH_VALUE_SOLD);

        $args = array(
            'numberposts'      => $intLimit,
            'post_status'      => array('publish'),
            'post_type'        => 'lot',
            'bp_orderby'       => $isSold ? 'soldBid' : 'estimatedPriceInt',
            'order'            => 'DESC',
            'suppress_filters' => false,
            'tax_query'        => array(
                array(
                    'taxonomy' => 'auction_number',
                    'field'    => 'slug',
                    'terms'    => $slugs,
                ),
            ),
        );

        if ($isSold) {
            $args[LotMetaData::BP_QUERY] = array(
                'bp_field' => array(
                    array(
                        'field'   => 'soldBid',
                        'value'   => 0,
                        'compare' => '>',
                    ),
                ),
            );

            // Over-fetch to absorb false-positive sold lots filtered out below.
            $args['numberposts'] = $intLimit + 10;
        }

        $args = apply_filters('bp_lot_list_high_value_args_filter', $args, $mode);

        $posts = get_posts($args);

        // Same false-positive guard as random_past: BidSpirit sometimes leaves
        // soldBid == startPrice on unsold lots, so verify real bidding occurred.
        if ($isSold && !empty($posts)) {
            $posts = array_filter($posts, function($lot) {
                $soldBid    = floatval(LotMetaData::getMetaField('soldBid', $lot->ID));
                $startPrice = floatval(LotMetaData::getMetaField('startPrice', $lot->ID));
                $leadingBid = floatval(LotMetaData::getMetaField('leadingBid', $lot->ID));

                return ($soldBid != $startPrice) || ($leadingBid > 0);
            });

            $posts = array_values($posts);

            if (count($posts) > $intLimit) {
                $posts = array_slice($posts, 0, $intLimit);
            }
        }

        return $posts;
    }

    public function init() {
        add_action('widgets_init', array($this, 'widget_load'));
        add_shortcode('bp_lot_list', array($this, 'lot_list_shortcode'));
    }

    public function widget_load() {
        register_widget($this);
    }

    /**
     * @return array
     */
    public function getDefaultArgs()
    {
        return $this->defaultArgs;
    }

    /**
     * @param  array  $defaultArgs
     */
    private function setDefaultArgs($limit)
    {
        $this->defaultArgs = array(
            "numberposts" => $limit,
            "post_status" => ["publish"],
            LotMetaData::BP_QUERY => [
                "bp_field" => [
                    [
                        "value" => 1,
                        "field" => "featured",
                        "compare" => "="
                    ]
                ],
            ],
            "post_type" => "lot",
            "bp_orderby" => Lot::VALUE_NUM_BS,
            "order" => "ASC",
            "suppress_filters" => false
        );
    }

    private function pluckSlug($auctions)
    {
        $slugs = [];
        if (!empty($auctions)) {
            foreach ($auctions as $auction) {
                $slugs[] = $auction->slug;
            }
        }
        return $slugs;
    }

    private function mergeArgs($args, $slugs)
    {
        return array_merge($args, [
            'tax_query' => [
                [
                    'taxonomy' => 'auction_number',
                    'field' => 'slug',
                    'terms' => $slugs
                ]
            ]
        ]);
    }
}
$lots = new LotsWidget();
$lots->init();
