<?php

namespace App\Models;

use Carbon\Carbon;
use DateTimeZone;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Cache;
use Morilog\Jalali\Jalalian;

class Inventory extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'storage_id',
        'product_id',
        'store_id',

        'uuid',
        'price',
        'discount_price',
        'discount_expire',
        'discount_tree',
        'count',
        'min_sale',
        'max_sale',
        'original',
        'used',
        'weight',
        'purchase_price',
        'price_changes',
        'image',
        'description',
        'send_time',
        'status',
    ];

    protected $casts = [
        'storage_id' => 'integer',
        'product_id' => 'integer',
        'store_id' => 'integer',
        'price' => 'integer',
        'discount_price' => 'integer',
        'count' => 'integer',
        'min_sale' => 'integer',
        'max_sale' => 'integer',
        'weight' => 'integer',
        'original' => 'boolean',
        'used' => 'boolean',
    ];

    /**
     * additional data
     */
    protected $appends = [
        'props',
        'raw_props',
        'get_storage',
        'get_image',
        'get_formatted_price',
        'get_formatted_discount_price',
        'get_formatted_count',
        'get_formatted_min_sale',
        'get_formatted_max_sale',
        'get_discount_tree',
        'get_discount_percent',
        'get_final_price',
        'get_formatted_final_price',
        'get_customer_club_score',
        'get_price_changes',
        'get_price_changes_chart',
        'is_discount_valid',
        'jalali_discount_expire',
        'jalali_created_at',
        'jalali_updated_at',

        'safe',
    ];

    public function getPropsAttribute()
    {
        return $this->inventoryProps()->get()->map(fn($inp) => [
            'id' => $inp->id,
            'name' => $inp->name,
            'type' => $inp->type,
            'value' => $inp->type == 'color' ? unserialize($inp->value) : $inp->value,
        ]);
    }

    public function getRawPropsAttribute()
    {
        $props = [];
        $inventoryProps = $this->inventoryProps()->get()->map(fn($prop) => [
            $prop->name => $prop->type == 'color' ? unserialize($prop->value)['color'] : $prop->value,
        ])->toArray();
        foreach ($inventoryProps as $inp) {
            $props = array_merge($inp, $props);
        }

        return array_reverse($props);
    }

    public function getGetStorageAttribute()
    {
        return [
            'id' => $this->storage->id,
            'uuid' => $this->storage->uuid,
            'name' => $this->storage->name,
            'address' => $this->storage->address,
        ];
    }

    public function getGetImageAttribute()
    {
        $image = [
            'id' => null,
            'url' => null,
            'alt' => null,
        ];

        $file = null;
        if ($this->image != null) {
            $file = Cache::remember('files_' . $this->image, now()->addMonth(), function () {
                return File::find($this->image);
            });
        }

        if ($file != null && $file->type == "image") {
            $image['id'] = $file->id;
            $image['url'] = asset($file->url);
            $image['alt'] = $file->description;
        } else {
            $image['id'] = null;
            $image['url'] = $this->product->get_images[0]['link'];
            $image['alt'] = $this->product->get_images[0]['alt'];
        }

        return $image;
    }

    public function getGetFormattedPriceAttribute()
    {
        return number_format($this->price);
    }

    public function getGetFormattedDiscountPriceAttribute()
    {
        return $this->discount_price != null ? number_format($this->discount_price) : null;
    }

    public function getGetFormattedCountAttribute()
    {
        return $this->count != null && $this->count != '' ? number_format($this->count) : null;
    }

    public function getGetFormattedMinSaleAttribute()
    {
        return $this->min_sale != null && $this->min_sale != '' ? number_format($this->min_sale) : null;
    }

    public function getGetFormattedMaxSaleAttribute()
    {
        return $this->max_sale != null && $this->max_sale != '' ? number_format($this->max_sale) : null;
    }

    public function getGetDiscountTreeAttribute()
    {
        return $this->discount_tree != null && $this->discount_tree != '' ? unserialize($this->discount_tree) : [];
    }

    public function getGetDiscountPercentAttribute()
    {
        return $this->discount_price != null ? round((($this->price - $this->discount_price) * 100) / $this->price) : null;
    }

    public function getGetFinalPriceAttribute()
    {
        return $this->is_discount_valid ? $this->discount_price : $this->price;
    }

    public function getGetFormattedFinalPriceAttribute()
    {
        return number_format($this->is_discount_valid ? $this->discount_price : $this->price);
    }

    public function getGetCustomerClubScoreAttribute()
    {
        // get customer club (price and score)
        $customerClub = Cache::remember('setting_customer_club', now()->addMonth(), function () {
            return Setting::where('key', 'customer_club')->first();
        });
        $customerClub = $customerClub != null ? unserialize($customerClub->value) : ['price' => 0, 'score' => 0];
        $score = $customerClub['price'] > 0 && $customerClub['score'] > 0 ? round(($this->get_final_price / str_replace(',', '', $customerClub['price'])) * str_replace(',', '', $customerClub['score'])) : 0;

        return [
            'score' => $score,
            'formatted_score' => number_format($score),
        ];
    }

    public function getGetPriceChangesAttribute()
    {
        return $this->price_changes != null ? unserialize($this->price_changes) : null;
    }

    public function getGetPriceChangesChartAttribute()
    {
        $priceChanges = $this->price_changes != null ? unserialize($this->price_changes) : null;

        if ($priceChanges != null) {
            $price = array_values($priceChanges);
            $date = array_map(function ($date) {
                return Jalalian::forge(Carbon::createFromTimestamp($date))->format('y/m/d');
            }, array_keys($priceChanges));

            return [
                'price' => $price,
                'date' => $date,
            ];
        } else {
            return [
                'price' => [],
                'date' => [],
            ];;
        }
    }

    public function getIsDiscountValidAttribute()
    {
        if ($this->discount_price != null) {
            $date = $this->discount_expire != null ? $this->discount_expire : null;

            if ($date != null) {
                if (Jalalian::forge($date, new DateTimeZone("Asia/Tehran"))->getTimestamp() >= Jalalian::forge(Carbon::now(), new DateTimeZone("Asia/Tehran"))->getTimestamp())
                    return true;
                else
                    return false;
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    public function getJalaliDiscountExpireAttribute()
    {
        $date = [
            Jalalian::forge($this->discount_expire)->format('%d %B %Y'),
            Jalalian::forge($this->discount_expire)->format('Y/m/d'),
            Jalalian::forge($this->discount_expire)->format('H:i - Y/m/d'),
            Jalalian::forge($this->discount_expire)->ago(),
            Jalalian::forge($this->discount_expire)->getTimestamp(),
        ];
        return $this->discount_expire != null ? $date : null;
    }

    public function getJalaliCreatedAtAttribute()
    {
        $date = [
            Jalalian::forge($this->created_at)->format('%d %B %Y'),
            Jalalian::forge($this->created_at)->format('Y/m/d'),
            Jalalian::forge($this->created_at)->format('H:i - Y/m/d'),
            Jalalian::forge($this->created_at)->ago(),
            Jalalian::forge($this->created_at)->getTimestamp(),
        ];
        return $date;
    }

    public function getJalaliUpdatedAtAttribute()
    {
        $date = [
            Jalalian::forge($this->updated_at)->format('%d %B %Y'),
            Jalalian::forge($this->updated_at)->format('Y/m/d'),
            Jalalian::forge($this->updated_at)->format('H:i - Y/m/d'),
            Jalalian::forge($this->updated_at)->ago(),
            Jalalian::forge($this->updated_at)->getTimestamp(),
        ];
        return $date;
    }

    public function getSafeAttribute()
    {
        return [
            'id' => $this->id,
            'uuid' => $this->uuid,
            'price' => $this->price,
            'discount_price' => $this->discount_price,
            'count' => $this->count,
            'min_sale' => $this->min_sale,
            'max_sale' => $this->max_sale,
            'price_changes' => $this->price_changes,
            'status' => $this->status,
            'get_formatted_price' => $this->get_formatted_price,
            'get_formatted_discount_price' => $this->get_formatted_discount_price,
            'jalali_discount_expire' => $this->jalali_discount_expire,
            'get_formatted_count' => $this->get_formatted_count,
            'get_formatted_min_sale' => $this->get_formatted_min_sale,
            'get_formatted_max_sale' => $this->get_formatted_max_sale,
            'get_discount_percent' => $this->get_discount_percent,
            'get_final_price' => $this->get_final_price,
            'get_formatted_final_price' => $this->get_formatted_final_price,
            'get_price_changes' => $this->get_price_changes,
            'get_price_changes_chart' => $this->get_price_changes_chart,
            'raw_props' => $this->raw_props,

            'storage' => $this->storage->safe,
            'store' => $this->store != null ? $this->store->safe : null,
        ];
    }
    /** end append */

    /**
     * remove cache data in update
     */
    protected static function boot()
    {
        parent::boot();

        static::saved(function ($inventory) {
            // product appends
            Cache::forget('product_inventory_' . $inventory->product_id);
            Cache::forget('product_inventory_status_' . $inventory->product_id);
            Cache::forget('product_has_discount_' . $inventory->product_id);
            Cache::forget('product_best_price_' . $inventory->product_id);
            Cache::forget('product_unique_color_' . $inventory->product_id);
            Cache::forget('product_best_inventory_' . $inventory->product_id);
            Cache::forget('product_all_images_' . $inventory->product_id);

            // get product props (show product page)
            Cache::forget('get_product_props_' . $inventory->product_id);
            Cache::forget('get_product_inventories_' . $inventory->product_id);
        });

        static::deleted(function ($inventory) {
            // product appends
            Cache::forget('product_inventory_' . $inventory->product_id);
            Cache::forget('product_inventory_status_' . $inventory->product_id);
            Cache::forget('product_has_discount_' . $inventory->product_id);
            Cache::forget('product_best_price_' . $inventory->product_id);
            Cache::forget('product_unique_color_' . $inventory->product_id);
            Cache::forget('product_best_inventory_' . $inventory->product_id);
            Cache::forget('product_all_images_' . $inventory->product_id);

            // get product props (show product page)
            Cache::forget('get_product_props_' . $inventory->product_id);
            Cache::forget('get_product_inventories_' . $inventory->product_id);
        });
    }

    /* relationships **************/
    public function storage(): BelongsTo
    {
        return $this->belongsTo(Storage::class);
    }

    public function product(): BelongsTo
    {
        return $this->belongsTo(Product::class);
    }

    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }

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

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