<?php

namespace App\Models;

use App\Traits\HasMediaVersions;
use Cviebrock\EloquentSluggable\Sluggable;
use Cviebrock\EloquentSluggable\SluggableScopeHelpers;
use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
use Plank\Mediable\Mediable;

class Product extends Model implements Buyable
{

    use Sluggable,
        SluggableScopeHelpers,
        Mediable;

    protected $guarded = ['id'];

    protected $appends = ['excerpt'];

    protected $casts = [
        'features' => 'array',
    ];

    public static function boot()
    {
        parent::boot();

        static::deleting(function ($model) {
            // Delete media gallery
            if ($model->hasMedia('gallery')) {
                $model->getMedia('gallery')->each(function ($item, $key) {
                    $item->delete();
                });
            }

            $model->plans()->each(function ($plan) {
                $plan->delete();
            });

            $model->addons()->each(function ($addon) {
                $addon->delete();
            });

            $model->supportPolicy()->delete();

            $model->files()->each(function ($file) {
                $file->delete();
            });

            $model->coupons()->detach();

            $model->meta()->delete();
        });
    }

    /**
     * Return the sluggable configuration array for this model.
     *
     * @return array
     */
    public function sluggable(): array
    {
        return [
            'slug' => [
                'source' => 'name'
            ]
        ];
    }

    public function meta()
    {
        return $this->hasMany(ProductMeta::class);
    }

    public function user()
    {
        return $this->belongsTo('App\Models\User');
    }

    /**
     * This function is used to retrieve
     * formatted price with currency symbol
     *
     * @return string
     */
    public function getStartingPriceAttribute()
    {
        $defaultPlan = $this->plans()->where('is_default', true)->first();
        if ($defaultPlan) {
            $defaultPricingTier = $defaultPlan->pricingTiers()->where('is_default', true)->first();
            if ($defaultPricingTier) {
                return $defaultPricingTier->price;
            }
            return $defaultPlan->pricingTiers()->first()->price;
        }

        $firstPlan = $this->plans()->first();
        return $firstPlan?->pricingTiers()->first()->price;
    }

    public function getProductMetaAttribute()
    {
        return $this->meta()->pluck('value', 'key')->all();
    }

    public function scopePublished($query)
    {
        $query->where('status', 'publish');
    }

    public function scopeUnPublished($query)
    {
        $query->where('status', 'draft');
    }

    public function hasMeta($key)
    {
        $meta = $this->meta;

        foreach ($meta as $item):
            if ($item->key == $key && !is_null($item->value)) return true;
        endforeach;

        return false;
    }

    public function getMeta($key)
    {
        $meta = $this->meta;

        foreach ($meta as $item):
            if ($item->key == $key) return $item->value;
        endforeach;

        return null;
    }

    /**
     * Get the identifier of the Buyable item.
     *
     * @return int|string
     */
    public function getBuyableIdentifier($options = null)
    {
        return $this->id;
    }

    /**
     * Get the description or name of the Buyable item.
     *
     * @return string
     */
    public function getBuyableDescription($options = null)
    {
        return $this->name;
    }

    /**
     * Get the price of the Buyable item.
     *
     * @return float
     */
    public function getBuyablePrice($options = null)
    {
        return $this->price;
    }

    /**
     * @param string $slug
     * @param array $columns
     * @return mixed
     */
    public static function findBySlug(string $slug, array $columns = ['*'])
    {
        $slug = utf8_uri_encode($slug);

        return static::whereSlug($slug)->first($columns);
    }

    /**
     * @param string $slug
     * @param array $columns
     * @return mixed
     */
    public static function findBySlugOrFail(string $slug, array $columns = ['*'])
    {
        $slug = utf8_uri_encode($slug);

        return static::whereSlug($slug)->firstOrFail($columns);
    }

    public function coupons()
    {
        return $this->belongsToMany(Coupon::class);
    }

    public function plans(): HasMany
    {
        return $this->hasMany(Plan::class);
    }

    public function hasMultiplePlans()
    {
        return $this->plans()->count() > 1;
    }

    public function defaultPlan()
    {
        return $this->belongsTo(Plan::class, 'default_plan_id');
    }

    public function getFeaturedImage()
    {
        return $this->hasMedia('gallery') ?
            $this->firstMedia('gallery')->getUrl('medium') :
            null;
    }

    public function hasFeaturedImage()
    {
        return $this->hasMedia('gallery');
    }

    public function addons()
    {
        return $this->hasMany(Addon::class);
    }

    public function allAddons()
    {
        return $this->hasMany(Addon::class);
    }

    public function getImageUrl(string $size = 'medium'): string
    {
        return $this->hasMedia('gallery')
            ? $this->firstMedia('gallery')->getUrl($size)
            : get_placeholder_img();
    }

    public function files(): HasMany
    {
        return $this->hasMany(ProductFile::class)->orderBy('created_at', 'desc');
    }

    public function category()
    {
        return $this->belongsTo(ProductCategory::class, 'category_id');
    }

    public function faqs()
    {
        return $this->hasMany(Faq::class);
    }

    public function reviews()
    {
        return $this->hasMany(Review::class);
    }

    public function supportPolicy()
    {
        return $this->hasOne(SupportPolicy::class);
    }

    public function getExcerptAttribute()
    {
        // 81 is the length of your example string
        return strip_tags(Str::limit($this->description, 100, '...'));
    }

    // reviews count
    public function getReviewsCountAttribute()
    {
        return $this->reviews()->count();
    }

    // average rating
    public function getAverageRatingAttribute()
    {
        if (array_key_exists('reviews_avg_rating', $this->attributes)) {
            return $this->attributes['reviews_avg_rating'] ?: 0;
        }

        return $this->reviews()->avg('rating') ?: 0;
    }

    public function isBundle(): bool
    {
        $features = is_array($this->features) ? $this->features : [];
        return (bool) ($features['bundle'] ?? false);
    }

    public function getCheckoutFields(): array
    {
        if (!$this->hasMeta('checkout_fields')) {
            return [];
        }

        $raw = $this->getMeta('checkout_fields');

        if (is_array($raw)) {
            return $raw;
        }

        return json_decode($raw, true) ?? [];
    }

    public function hasCheckoutFields(): bool
    {
        return !empty($this->getCheckoutFields());
    }
}
