<?php

namespace SoftminalB2B\Models;

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

/**
 * A buy-X-get-Y promotion (competitive gap feature).
 *
 * Free ships product-triggered rules whose reward is more of the same product,
 * added free or at a discount. Pro adds category triggers and cross-product
 * gifts, which is why `buy_type` and the reward-type range exist here from the
 * start rather than arriving as a later migration — core recognises the values
 * so Pro can create them and the cart engine can honour them without a schema
 * change.
 *
 * @property int|null    $id
 * @property string      $title
 * @property string      $buy_type
 * @property int         $buy_id
 * @property int         $buy_qty
 * @property string      $reward_type
 * @property int         $reward_qty
 * @property string|null $reward_value
 * @property string      $reward_scope
 * @property int         $reward_product_id
 * @property int|null    $group_id
 * @property bool        $repeat_reward
 * @property bool        $active
 * @property int         $priority
 */
class BogoRule extends Model
{
    protected $table = 'bogo_rules';
    protected $timestamps = true;

    protected $fillable = [
        'title',
        'buy_type',
        'buy_id',
        'buy_qty',
        'reward_type',
        'reward_qty',
        'reward_value',
        'reward_scope',
        'reward_product_id',
        'group_id',
        'repeat_reward',
        'active',
        'priority',
    ];

    protected $casts = [
        'repeat_reward' => 'boolean',
        'active'        => 'boolean',
    ];

    /** Trigger is a single product. */
    public const BUY_PRODUCT = 'product';

    /** Trigger is any product in a category — Pro. */
    public const BUY_CATEGORY = 'category';

    /** Reward units are free. */
    public const REWARD_FREE = 'free';

    /** Reward units get a percentage off. */
    public const REWARD_PERCENT = 'percent';

    /** Reward units get a fixed amount off (in store currency). */
    public const REWARD_AMOUNT = 'amount';

    /** Reward is more of the trigger product (the only Free option). */
    public const REWARD_SCOPE_SAME = 'same';

    /** Reward is a specific, different product — Pro. */
    public const REWARD_SCOPE_PRODUCT = 'product';

    /** Reward is the cheapest qualifying item already in the cart — Pro. */
    public const REWARD_SCOPE_CHEAPEST = 'cheapest';

    /**
     * Every trigger type the schema understands. Category is Pro — the
     * controller gates it; core still recognises the value.
     *
     * @return string[]
     */
    public static function buyTypes(): array
    {
        return [self::BUY_PRODUCT, self::BUY_CATEGORY];
    }

    /** Trigger types the free version can create. */
    public static function freeBuyTypes(): array
    {
        return [self::BUY_PRODUCT];
    }

    /**
     * @return string[]
     */
    public static function rewardTypes(): array
    {
        return [self::REWARD_FREE, self::REWARD_PERCENT, self::REWARD_AMOUNT];
    }

    /**
     * @return string[]
     */
    public static function rewardScopes(): array
    {
        return [self::REWARD_SCOPE_SAME, self::REWARD_SCOPE_PRODUCT, self::REWARD_SCOPE_CHEAPEST];
    }

    /** Reward scopes the free version can create; the rest need Pro. */
    public static function freeRewardScopes(): array
    {
        return [self::REWARD_SCOPE_SAME];
    }

    /**
     * Whether the free core, on its own, fully understands this rule — a
     * product trigger rewarding more of the same product. Everything else is a
     * Pro rule that Pro's own engine reconciles; core leaves those gift lines
     * alone so the two never fight over the same cart line.
     */
    public function isCoreOwned(): bool
    {
        return $this->buy_type === self::BUY_PRODUCT
            && $this->reward_scope === self::REWARD_SCOPE_SAME;
    }

    public function group()
    {
        return $this->belongsTo(Group::class, 'group_id');
    }

    public function rewardUnitsFor(int $triggerQty): int
    {
        $buyQty    = max(1, (int) $this->buy_qty);
        $rewardQty = max(1, (int) $this->reward_qty);

        if ($triggerQty < $buyQty) {
            return 0;
        }

        if (!$this->repeat_reward) {
            return $rewardQty;
        }

        return intdiv($triggerQty, $buyQty) * $rewardQty;
    }

    /**
     * The price a single reward unit should be charged, given the buyer's
     * resolved unit price. Never below zero.
     */
    public function rewardPriceFor(float $unitPrice): float
    {
        switch ($this->reward_type) {
            case self::REWARD_PERCENT:
                $percent = min(100.0, max(0.0, (float) $this->reward_value));
                return round(max(0.0, $unitPrice * (1 - $percent / 100)), wc_get_price_decimals());

            case self::REWARD_AMOUNT:
                $amount = max(0.0, (float) $this->reward_value);
                return round(max(0.0, $unitPrice - $amount), wc_get_price_decimals());

            case self::REWARD_FREE:
            default:
                return 0.0;
        }
    }
}
