<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;

class Coupon extends BaseModel
{
    use HasFactory;

    protected $with = ['couponUsages'];
    protected $appends = ['coupon_usage_count'];

    protected $fillable = [
        'name',
        'code',
        'discount',
        'limit',
        'type',
        'minimum_spend',
        'maximum_spend',
        'limit_per_user',
        'expiry_date',
        'description',
        'is_active',
        'included_plan',
        'excluded_plan',
        'is_deleted',
    ];

    protected $casts = [
        'included_plan' => 'array',
        'excluded_plan' => 'array',
        'expiry_date' => 'datetime',
    ];

    public function couponUsages()
    {
        return $this->hasMany(CouponUsage::class);
    }

    public function getCouponUsageCountAttribute()
    {
        return $this->couponUsages()->count();
    }

    /**
     * Get the plans that are included in this coupon.
     */
    public function plans()
    {
        return $this->belongsToJsonMany(Plan::class, 'included_plan');
    }

    /**
     * Get the plans that are excluded from this coupon.
     */
    public function excludedPlans()
    {
        return $this->belongsToJsonMany(Plan::class, 'excluded_plan');
    }

    /**
     * Define a belongs-to-many relationship based on JSON array columns.
     * This is a custom relation that can be eager loaded.
     */
    protected function belongsToJsonMany($related, $jsonColumn)
    {
        $instance = new $related;
        $relatedKey = $instance->getKeyName();
        return new class($instance->newQuery(), $this, $jsonColumn, $relatedKey) extends Relation {
            protected $jsonColumn;
            protected $relatedKeyName;
            public function __construct($query, $parent, $jsonColumn, $relatedKeyName)
            {
                parent::__construct($query, $parent);
                $this->jsonColumn = $jsonColumn;
                $this->relatedKeyName = $relatedKeyName;
            }
            public function addConstraints()
            {
                if (static::$constraints) {
                    $jsonIds = $this->parent->{$this->jsonColumn} ?? [];
                    if (!empty($jsonIds)) {
                        $this->query->whereIn($this->relatedKeyName, $jsonIds);
                    } else {
                        // Empty result if no IDs exist
                        $this->query->whereRaw('1 = 0');
                    }
                }
            }
            public function addEagerConstraints(array $models)
            {
                $allIds = [];
                // Collect all unique IDs from all models' JSON columns
                foreach ($models as $model) {
                    $ids = $model->{$this->jsonColumn} ?? [];
                    if (!empty($ids)) {
                        $allIds = array_merge($allIds, $ids);
                    }
                }
                $allIds = array_unique(array_filter($allIds));

                if (!empty($allIds)) {
                    $this->query->whereIn($this->relatedKeyName, $allIds);
                }
            }
            public function initRelation(array $models, $relation)
            {
                foreach ($models as $model) {
                    $model->setRelation($relation, $this->related->newCollection());
                }
                return $models;
            }
            public function match(array $models, \Illuminate\Database\Eloquent\Collection $results, $relation)
            {
                if ($results->isEmpty()) {
                    return $models;
                }
                // Index the results by their keys for faster lookups
                $dictionary = $results->keyBy($this->relatedKeyName)->all();
                // Match results to models
                foreach ($models as $model) {
                    $jsonIds = $model->{$this->jsonColumn} ?? [];
                    $matches = collect();
                    foreach ($jsonIds as $id) {
                        if (isset($dictionary[$id])) {
                            $matches->push($dictionary[$id]);
                        }
                    }
                    $model->setRelation($relation, $matches);
                }
                return $models;
            }
            public function getResults()
            {
                $jsonIds = $this->parent->{$this->jsonColumn} ?? [];
                if (empty($jsonIds)) {
                    return $this->related->newCollection();
                }
                return $this->query->get();
            }
        };
    }

    /**
     * Scope a query to only include active coupons.
     */
    public function scopeActive(Builder $query)
    {
        return $query->where('is_active', 1)
            ->where('is_deleted', 0);  // Ensure we only get active and non-deleted coupons
    }


    /**
     * Scope a query to only include coupons that include a specific plan.
     */
    public function scopeIncludesPlan($query, $planId)
    {
        if (is_array($planId)) {
            return $query->where(function ($q) use ($planId) {
                foreach ($planId as $id) {
                    $q->orWhereJsonContains('included_plan', $id);
                }
            });
        }
        return $query->whereJsonContains('included_plan', $planId);
    }

    /**
     * Scope a query to only include coupons that exclude a specific plan.
     */
    public function scopeExcludesPlan($query, $planId)
    {
        if (is_array($planId)) {
            return $query->where(function ($q) use ($planId) {
                foreach ($planId as $id) {
                    $q->orWhereJsonContains('excluded_plan', $id);
                }
            });
        }
        return $query->whereJsonContains('excluded_plan', $planId);
    }



    /**
     * Check if this coupon includes a specific plan.
     */
    public function includesPlan($planId)
    {
        return is_array($this->included_plan) && in_array($planId, $this->included_plan);
    }

    /**
     * Check if this coupon excludes a specific plan.
     */
    public function excludesPlan($planId)
    {
        return is_array($this->excluded_plan) && in_array($planId, $this->excluded_plan);
    }
}
