<?php

namespace App\Models;

use App\Traits\HasTranslations;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
 * @property int $id
 * @property int $group_id
 * @property string $type
 * @property string $slug
 * @property string $name
 * @property string|null $description
 * @property int $hidden
 * @property string $status
 * @property int $require_domain
 * @property int $stock_control
 * @property int|null $total_stock
 * @property int|null $used_stock
 * @property string|null $payment_type
 * @property string|null $setup_type
 * @property string|null $module
 * @property int|null $auto_suspension_days
 * @property int|null $auto_termination_days
 * @property int $affiliate_enabled
 * @property int $is_featured
 * @property int $free_domain_enabled
 * @property int|null $server_group
 * @property int|null $trial_period_days
 * @property int $trial_ccsave
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 *
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product query()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereAffiliateEnabled($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereAutoSuspensionDays($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereAutoTerminationDays($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereDescription($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereFreeDomainEnabled($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereGroupId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereHidden($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereIsFeatured($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereModule($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereName($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product wherePaymentType($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereRequireDomain($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereServerGroup($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereSetupType($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereSlug($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereStatus($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereStockControl($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereTotalStock($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereTrialCcsave($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereTrialPeriodDays($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereType($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereUpdatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereUsedStock($value)
 *
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientProduct> $clientProducts
 * @property-read int|null $client_products_count
 * @property-read \App\Models\ProductGroup|null $group
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductFreeDomainTld> $productFreeDomainTlds
 * @property-read int|null $product_free_domain_tlds_count
 * @property int|null $category_id
 * @property int|null $order
 * @property bool $adaptive_pricing
 * @property bool $tax_excluded
 * @property string $trial_mode
 * @property float|null $trial_paid_discount_percent
 * @property string|null $trial_default_period
 * @property-read \App\Models\ProductCategory|null $category
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductConfigOption> $options
 * @property-read int|null $options_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Pricing> $pricing
 * @property-read int|null $pricing_count
 *
 * @method static \Database\Factories\ProductFactory factory($count = null, $state = [])
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereAdaptivePricing($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereCategoryId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereOrder($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereTaxExcluded($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereTrialMode($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereTrialPaidDiscountPercent($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Product whereTrialDefaultPeriod($value)
 *
 * @property bool $hide_setup_fees
 * @property string $auto_setup_option
 * @property string|null $welcome_email
 * @property int $setup_delay Setup delay in minutes
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductConfigOptionGroup> $configOptionGroups
 * @property-read int|null $config_option_groups_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductConfigOption> $configOptions
 * @property-read int|null $config_options_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductCustomField> $customFields
 * @property-read int|null $custom_fields_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProductModuleConfig> $moduleConfigs
 * @property-read int|null $module_configs_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\LanguageTranslation> $translations
 * @property-read int|null $translations_count
 *
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereAutoSetupOption($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereHideSetupFees($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereSetupDelay($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereTranslation(string $locale, string $field, string $value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereTranslationLike(string $locale, string $field, string $value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Product whereWelcomeEmail($value)
 *
 * @mixin \Eloquent
 */
class Product extends Model
{
    use HasFactory, HasTranslations;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'category_id',
        'group_id',
        'type',
        'slug',
        'name',
        'description',
        'hidden',
        'status',
        'require_domain',
        'stock_control',
        'total_stock',
        'used_stock',
        // PRC-812 — free/trial suistimal kontrolleri
        'max_orders_per_client',
        'allow_renewal',
        'payment_type',
        'setup_type',
        'module',
        'auto_suspension_days',
        'auto_termination_days',
        'downgrade_credit_mode',
        'same_product_downgrade_refund',
        'refund_dropped_options',
        'upgrades_enabled',
        // PRC-697 — pro-rata (ürün override): sabit yenileme günü + aç/kapa.
        'proration_enabled',
        'billing_anchor_day',
        'affiliate_enabled',
        'is_featured',
        'free_domain_enabled',
        'server_group',
        'trial_period_days',
        'trial_ccsave',
        'trial_mode',
        'trial_paid_discount_percent',
        'trial_paid_discount_periods',
        'trial_default_period',
        'adaptive_pricing',
        'adaptive_term_pricing',
        'hide_setup_fees',
        'auto_setup_option',
        'welcome_email',
        'setup_delay',
        'tax_excluded',
        'requires_ip',
        // PRC-382 / M4 — per-product late fee override
        'late_fee_mode',
        'late_fee_type',
        'late_fee_amount',
        'late_fee_minimum',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'hidden' => 'boolean',
        'requires_ip' => 'boolean',
        'require_domain' => 'boolean',
        // PRC-812 — free/trial suistimal kontrolleri
        'max_orders_per_client' => 'integer',
        'allow_renewal' => 'boolean',
        'stock_control' => 'boolean',
        'affiliate_enabled' => 'boolean',
        'is_featured' => 'boolean',
        'free_domain_enabled' => 'boolean',
        'same_product_downgrade_refund' => 'boolean',
        'refund_dropped_options' => 'boolean',
        'upgrades_enabled' => 'boolean',
        // PRC-697 — nullable boolean; NULL = grup fallback / global setting.
        'proration_enabled' => 'boolean',
        'billing_anchor_day' => 'integer',
        'trial_ccsave' => 'boolean',
        'trial_paid_discount_percent' => 'decimal:2',
        // PRC-544 — JSON array of period months (e.g. [1, 12]); NULL = all periods.
        'trial_paid_discount_periods' => 'array',
        'adaptive_pricing' => 'boolean',
        'adaptive_term_pricing' => 'boolean',
        'hide_setup_fees' => 'boolean',
        'tax_excluded' => 'boolean',
        // PRC-382 / M4 — money columns cast to float for arithmetic.
        'late_fee_amount' => 'float',
        'late_fee_minimum' => 'float',
    ];

    /**
     * Çevrilebilir alanlar
     *
     * @var array
     */
    protected $translatable = ['name', 'description'];

    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'adaptive_pricing' => true,
    ];

    /**
     * Get the category that owns the product.
     */
    public function category(): BelongsTo
    {
        return $this->belongsTo(ProductCategory::class, 'category_id');
    }

    /**
     * Get the group that owns the product.
     */
    public function group(): BelongsTo
    {
        return $this->belongsTo(ProductGroup::class, 'group_id');
    }

    /**
     * Get the client products for the product.
     */
    public function clientProducts(): HasMany
    {
        return $this->hasMany(ClientProduct::class);
    }

    /**
     * Get the product free domain TLDs for the product.
     */
    public function productFreeDomainTlds(): HasMany
    {
        return $this->hasMany(ProductFreeDomainTld::class);
    }

    /**
     * Get the options for the product.
     */
    public function options(): BelongsToMany
    {
        return $this->belongsToMany(ProductConfigOption::class, 'product_config_option_relations', 'product_id', 'option_id')
            ->withPivot('group_id')
            ->withTimestamps();
    }

    /**
     * Get the pricing for the product.
     */
    public function pricing(): HasMany
    {
        return $this->hasMany(Pricing::class, 'relation_id')
            ->where('type', 'product');
    }

    /**
     * Get the module configurations for the product.
     */
    public function moduleConfigs(): HasMany
    {
        return $this->hasMany(ProductModuleConfig::class);
    }

    /**
     * Get the custom fields for the product.
     */
    public function customFields(): HasMany
    {
        return $this->hasMany(ProductCustomField::class)->ordered();
    }

    /**
     * Ürünün modül konfigürasyonlarını anahtar-değer çifti olarak al
     */
    public function getModuleConfigArray(?string $moduleName = null): array
    {
        return ProductModuleConfig::getConfigForProduct($this->id, $moduleName ?: $this->module);
    }

    /**
     * Get the config option groups for the product.
     */
    public function configOptionGroups(): BelongsToMany
    {
        return $this->belongsToMany(ProductConfigOptionGroup::class, 'product_config_option_relations', 'product_id', 'group_id')
            ->withPivot('option_id')
            ->withTimestamps()
            ->distinct()
            ->with(['configOptions' => function ($query) {
                $query->where('hidden', false);
            }, 'configOptions.configValues' => function ($query) {
                $query->where('status', true);
            }]);
    }

    /**
     * Get the config options for the product.
     */
    public function configOptions(): BelongsToMany
    {
        return $this->belongsToMany(ProductConfigOption::class, 'product_config_option_relations', 'product_id', 'option_id')
            ->withPivot('group_id')
            ->withTimestamps()
            ->with(['configValues' => function ($query) {
                $query->where('status', true);
            }]);
    }

    /**
     * Contracts (ToS / Privacy / SLA / DPA) attached to this product.
     */
    public function contracts(): BelongsToMany
    {
        return $this->belongsToMany(\App\Models\Contract::class, 'product_contracts')
            ->withPivot('required')
            ->withCasts(['required' => 'boolean'])
            ->withTimestamps();
    }

    public function upgradePathsAsSource(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(ProductUpgradePath::class, 'source_product_id');
    }

    public function upgradePathsAsTarget(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(ProductUpgradePath::class, 'target_product_id');
    }

    /**
     * Alias for upgradePathsAsSource — paths where this product is the source.
     * Used by the upgrade-paths API controller.
     */
    public function upgradeTargets(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(\App\Models\ProductUpgradePath::class, 'source_product_id');
    }

    /**
     * Whether the product still has stock available. Returns true when
     * stock_control is off (unlimited) or available stock > 0.
     */
    public function isInStock(): bool
    {
        if (! $this->stock_control) {
            return true;
        }
        if ($this->total_stock === null) {
            return true;
        }

        return ((int) $this->used_stock) < (int) $this->total_stock;
    }

    /**
     * Remaining stock count. Null = unlimited (stock_control off or total_stock unset).
     */
    public function availableStock(): ?int
    {
        if (! $this->stock_control || $this->total_stock === null) {
            return null;
        }

        return max(0, (int) $this->total_stock - (int) $this->used_stock);
    }

    /**
     * PRC-787 — isInStock()'un query karşılığı: listeleme sorgularında
     * stoğu biten (stock_control açık + used ≥ total) ürünleri eler.
     * Mantık isInStock() ile birebir: stok kontrolü kapalı VEYA total null
     * VEYA kullanılan < toplam.
     */
    public function scopeInStock($query)
    {
        return $query->where(function ($q) {
            $q->where('stock_control', false)
                ->orWhereNull('total_stock')
                ->orWhereRaw('COALESCE(used_stock, 0) < total_stock');
        });
    }

    /**
     * PRC-382 / M4 — Resolve the effective late fee config for this
     * product. Mode = 'disabled' returns an explicit disabled marker so
     * the InvoiceService can skip the item. Mode = 'custom' returns the
     * stored per-product values. Mode = 'inherit' (or anything else)
     * returns the global settings, so the caller doesn't need to know
     * which mode is in effect — only whether to bill and at what rate.
     *
     * Shape: ['enabled' => bool, 'type' => fixed|percentage, 'amount' => float, 'minimum' => float]
     */
    public function effectiveLateFee(): array
    {
        $mode = $this->late_fee_mode ?? 'inherit';

        if ($mode === 'disabled') {
            return ['enabled' => false, 'type' => 'fixed', 'amount' => 0.0, 'minimum' => 0.0];
        }

        if ($mode === 'custom') {
            $type = $this->late_fee_type ?? 'fixed';
            $amount = (float) ($this->late_fee_amount ?? 0);
            $minimum = (float) ($this->late_fee_minimum ?? 0);

            return [
                'enabled' => $amount > 0,
                'type' => $type,
                'amount' => $amount,
                'minimum' => $minimum,
            ];
        }

        // inherit — fall back to global settings
        $globalType = \App\Models\Setting::getValue('late_fee_type', 'disabled');
        if ($globalType === 'disabled' || ! \App\Models\Setting::getValue('late_fees_enabled', true)) {
            return ['enabled' => false, 'type' => 'fixed', 'amount' => 0.0, 'minimum' => 0.0];
        }

        return [
            'enabled' => true,
            'type' => $globalType,
            'amount' => (float) \App\Models\Setting::getValue('late_fee_amount', 0),
            'minimum' => (float) \App\Models\Setting::getValue('late_fee_minimum', 0),
        ];
    }
}
