<?php

namespace SoftminalB2B\Models;

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

use SoftminalB2B\Models\Concerns\InvalidatesPricing;

/**
 * One price adjustment. The Pricing\Resolver (week 2-3) walks these in
 * precedence order; nothing else may mutate Woo prices.
 *
 * @property int|null    $id
 * @property int|null    $group_id
 * @property string      $scope
 * @property int         $object_id
 * @property string      $type
 * @property string      $value
 * @property int         $min_qty
 * @property int|null    $max_qty
 * @property int         $priority
 * @property bool        $active
 */
class PriceRule extends Model
{
    use InvalidatesPricing;

    protected $table = 'price_rules';
    protected $timestamps = true;

    protected $fillable = [
        'group_id',
        'scope',
        'object_id',
        'type',
        'value',
        'min_qty',
        'max_qty',
        'priority',
        'active',
        // Nullable rule ownership (DB 1.8.0) — the Dokan bridge stamps a vendor's
        // own rules in Phase 5 self-service; admin-owned rules leave them null.
        'owner_id',
        'owner_type',
    ];

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

    public const SCOPE_GLOBAL   = 'global';
    public const SCOPE_CATEGORY = 'category';
    public const SCOPE_TAG      = 'tag';
    public const SCOPE_PRODUCT  = 'product';
    public const SCOPE_CUSTOMER = 'customer';

    /**
     * A whole marketplace vendor's catalogue (`object_id` = the vendor's WP user
     * id). Recognised by core so a vendor rule persists and enforces without a
     * schema change, but only ever created when the Dokan bridge is active — the
     * admin rule screens surface the "Vendor" scope off the API's vendor flag.
     * No new column: the generic `object_id` holds the vendor id.
     */
    public const SCOPE_VENDOR   = 'vendor';

    public const TYPE_FIXED      = 'fixed';
    public const TYPE_PERCENT    = 'percent';
    public const TYPE_AMOUNT_OFF = 'amount_off';

    /**
     * Most specific first — the resolver's precedence order.
     *
     * @return string[]
     */
    public static function scopes(): array
    {
        return [
            self::SCOPE_CUSTOMER,
            self::SCOPE_PRODUCT,
            self::SCOPE_TAG,
            self::SCOPE_CATEGORY,
            self::SCOPE_VENDOR,
            self::SCOPE_GLOBAL,
        ];
    }

    /**
     * @return string[]
     */
    public static function types(): array
    {
        return [self::TYPE_FIXED, self::TYPE_PERCENT, self::TYPE_AMOUNT_OFF];
    }

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

    public static function activeForGroup(?int $groupId)
    {
        $query = static::where('active', '=', 1);

        return $groupId === null
            ? $query->whereNull('group_id')->get()
            : $query->andWhere('group_id', '=', $groupId)->get();
    }

    /**
     * Whether this rule's quantity band contains $qty.
     */
    public function matchesQuantity(int $qty): bool
    {
        $min = (int) ($this->min_qty ?? 1);
        $max = $this->max_qty === null ? null : (int) $this->max_qty;

        return $qty >= $min && ($max === null || $qty <= $max);
    }
}
