<?php

namespace App\Models;

use App\Traits\LogOptionsTrait;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Storage;
use Spatie\Activitylog\Traits\LogsActivity;

/**
 * Class Product
 *
 * @property string|null $name
 * @property string|null $body
 * @property string|null $short_body
 * @property string|null $slug
 * @property array|null $colors
 * @property array|null $sizes
 * @property integer|null $price
 * @property boolean|null $published
 * @property integer|null $price_discount
 * @property integer|null $child_id
 * @property string|null $poster
 * @property string|null $poster_thumb
 * @property boolean|null $popular
 * @property boolean|null $leader_of_sales
 * @property integer $article_number
 * @property float $price_credit
 * @property string $currency
 * @property integer $brand_id
 * @property integer $color_id
 * @property integer $count
 * @property integer $measurement_id
 * @property array|null $keywords
 * @property array|null $descriptions
 * @property array|null $title_seo
 * @property boolean $available
 * @property string $data_sheet
 * @property boolean $is_ready_solution
 *
 * @property-read Category $category
 * @property-read Brand $brand
 * @property-read Color $color
 * @property-read Collection|Product[] $childrens
 * @property-read Collection|Product|BelongsTo $product
 * @property-read Collection|Product[] $childrensColor
 * @property-read Collection|Compilation[] $compilations
 * @property-read Collection|Comment[] $comments
 * @property-read Collection|Characteristic[] $characteristics
 * @property-read Collection|Basket[] $baskets
 * @property-read Collection|Screen[] $screens
 * @property-read Collection|Category[] $categories
 *
 * @property-read bool $is_favorite
 * @property-read bool $is_cart
 * @property-read bool $is_available
 * @property float $final_price
 * @property bool $diff_date
 * @property bool $in_cart
 *
 * @method getName(): string
 * @method getTitleSeo(): string
 * @method getBody(): string
 * @method getShortBody(): string
 * @method getKeywords(): string
 * @method getPrice(): float
 * @method getPoster(): string
 */
class Product extends Model
{
    use SoftDeletes, LogsActivity, LogOptionsTrait;

    protected $appends = ['isFavorite', 'diffDate', 'isCart', 'onCredit', 'isAvailable', 'finalPrice'];

    protected $guarded = ['id'];

    protected $casts = [
        'name' => 'array',
        'body' => 'array',
        'short_body' => 'array',
        'slug' => 'string',
        'sizes' => 'array',
        'poster' => 'string',
        'poster_thumb' => 'string',
        'price' => 'float',
        'price_discount' => 'float',
        'price_credit' => 'float',
        'currency' => 'string',
        'article_number' => 'string',
        'published' => 'boolean',
        'popular' => 'boolean',
        'leader_of_sales' => 'boolean',
        //'category_id' => 'array',
        'brand_id' => 'integer',
        'color_id' => 'integer',
        'child_id' => 'integer',
        'available' => 'boolean',
        'count' => 'integer',
        'keywords' => 'array',
        'descriptions' => 'array',
        'title_seo' => 'array',
        'is_ready_solution' => 'boolean'
    ];

    protected $hidden = [
        'child_id',
        'deleted_at'
    ];

    protected static $logName = 'products';
    protected static $logOnlyDirty = true;
    protected static $logAttributes = ['name', 'price', 'price_discount', 'article_number', 'published', 'available', 'count', 'title_seo', 'price_credit'];
    protected static $submitEmptyLogs = false;

    public function getFinalPriceAttribute()
    {
        if (!empty($this->price_discount)) {
            return $this->price_discount;
        } else {
            return $this->price;
        }
    }

    public function getInCartAttribute()
    {
        $user = getAuthUser();
        $userId = $user?->id;
        $token = request()->header('X-Application-Token');

        $cartQuery = Cart::where('product_id', $this->id);

        if (isset($userId)) {
            $cartQuery->where('user_id', $userId);

            $cart = $cartQuery->first();

            if ($cart) {
                return $cart->count;
            }
        }

        if (isset($token)) {
            $cartQuery->where('token', $token);

            $cart = $cartQuery->first();

            if ($cart) {
                return $cart->count;
            }
        }

        return 0;
    }

    public function getName(): string
    {
        if (App::isLocale('ru')) {
            return (string) $this->name['ru'];
        }

        return (string) $this->name['uz'];
    }

    public function getTitleSeo(): string
    {
        if (App::isLocale('ru')) {
            return (string) $this->title_seo['ru'];
        }

        return (string) $this->title_seo['uz'];
    }

    public function getBody(): string
    {
        if (App::isLocale('ru')) {
            return (string) $this->body['ru'];
        }

        return (string) $this->body['uz'];
    }

    public function getShortBody(): string
    {
        if (App::isLocale('ru')) {
            if (!empty($this->descriptions['ru'])) {
                return (string) $this->descriptions['ru'];
            }

            return (string) $this->short_body['ru'];
        }

        if (!empty($this->descriptions['uz'])) {
            return (string) $this->descriptions['uz'];
        }

        return (string) $this->short_body['uz'];
    }

    public function getKeywords(): string
    {
        if (App::isLocale('ru')) {
            if (!empty($this->keywords['ru'])) {
                $title = str_replace(' ', ', ', $this->getName());
                $keywords = $this->keywords['ru'];
                return "{$title}, {$keywords}";
            }
        }

        if (!empty($this->keywords['uz'])) {
            $title = str_replace(' ', ', ', $this->getName());
            $keywords = $this->keywords['uz'];
            return "{$title}, {$keywords}";
        }

        $title = str_replace(' ', ', ', $this->getName());
        $description = str_replace(' ', ', ', $this->getShortBody());

        return "{$title}, {$description}";
    }

    public function getPrice($precision = 0): float
    {
        return round($this->price * $this->getCurrency(), $precision);
    }

    public function getPoster(): string
    {
        if (!empty($this->poster)) {
            if (in_array(env('FILESYSTEM_DISK'), ['s3', 'minio'])) {
                return Storage::temporaryUrl(
                    $this->poster,
                    Date::now()->addMinutes(5)
                );
            }
            return '/' . $this->poster;
        }

        return '/images/no_product.jpg';
    }

    public function getDataSheet()
    {
        if (!empty($this->data_sheet) and ($this->data_sheet != null and $this->data_sheet != "null")) {
            if (in_array(env('FILESYSTEM_DISK'), ['s3', 'minio'])) {
                return Storage::temporaryUrl(
                    $this->data_sheet,
                    Date::now()->addMinutes(5)
                );
            }
            return '/' . $this->data_sheet;
        }
        return null;
    }

    public function getPosterThumb(): string
    {
        if (!empty($this->poster_thumb)) {
            if (in_array(env('FILESYSTEM_DISK'), ['s3', 'minio'])) {
                return Storage::temporaryUrl(
                    $this->poster_thumb,
                    Date::now()->addMinutes(5)
                );
            }
            return '/' . $this->poster_thumb;
        }

        return '/images/no_product.jpg';
    }

    /**
     * @return float
     */
    public function getDiscountPrice()
    {
        return round($this->price_discount * $this->getCurrency());
    }

    public function getOnCreditAttribute(): float
    {
        $price = $this->getPriceCredit();
        $credit = $price / 11;

        return abs($credit);
    }

    public function measurement(): BelongsTo
    {
        return $this->belongsTo(Measurement::class, 'measurement_id', 'id');
    }

    public function getPriceCredit(): float
    {
        return round($this->price_credit * $this->getCurrency());
    }

    private function pmt(float $rate, int $periods, float $present_value, float $future_value = 0.0, bool $beginning = false): float
    {
        $when = $beginning ? 1 : 0;

        if ($rate == 0) {
            return - ($future_value + $present_value) / $periods;
        }

        return - ($future_value + ($present_value * pow(1 + $rate, $periods)))
            /
            ((1 + $rate * $when) / $rate * (pow(1 + $rate, $periods) - 1));
    }

    public function getCurrency(): int
    {
        $currency = Currency::latest('id', 'desc')->limit(1)->first();

        if ($this->currency == 'dollar') {
            return $currency->dollar;
        } elseif ($this->currency == 'sum') {
            return 1;
        } else {
            return $currency->euro;
        }
    }

    public function getPriceFormat(): string
    {
        return number_format($this->price, 0, '', ' ');
    }

    public function getPriceDiscount(): int
    {
        return (int) $this->price_discount;
    }

    public function screen(): BelongsTo
    {
        return $this->belongsTo(Screen::class, 'id', 'product_id');
    }

    public function discount(): float
    {
        $a = $this->price;
        $b = $this->price_discount;

        $x = (($b * 100) / $a) - 100;

        return abs($x);
    }

    public function getDiffDate(): bool
    {
        $original_date = date_create(Carbon::parse($this->created_at)->format('Y-m-d'));
        $now = date_create(Carbon::now()->format('Y-m-d'));
        $diff = date_diff($original_date, $now);

        if ($diff->format("%a") <= 10) {
            return true;
        }

        return false;
    }

    public function categories(): BelongsToMany
    {
        return $this->belongsToMany(Category::class, 'categories_products');
    }

    public function getSlug(): string
    {
        return (string) $this->slug;
    }

    public function isAviable(): bool
    {
        return $this->published;
    }

    public function brand(): BelongsTo
    {
        return $this->belongsTo(Brand::class, 'brand_id', 'id')->withDefault([
            'name' => [
                'uz' => 'Brand yo`q',
                'ru' => 'Бренд нет'
            ]
        ]);
    }

    public function children(): HasOne
    {
        return $this->hasOne(self::class, 'child_id', 'id');
    }

    public function childrens(): HasMany
    {
        return $this->hasMany(self::class, 'child_id', 'id');
    }

    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class)->where('publish', true);
    }

    public function childrensColor(): HasMany
    {
        return $this->hasMany(self::class, 'child_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function color()
    {
        return $this->belongsTo(Color::class, 'color_id', 'id');
    }

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

    public function data(): HasOne
    {
        return $this->hasOne(self::class, 'child_id', 'id');
    }

    public function product(): BelongsTo
    {
        return $this->belongsTo(self::class, 'child_id', 'id');
    }

    public function compilations(): BelongsToMany
    {
        return $this->belongsToMany(Compilation::class, 'compilation_products');
    }

    public function getIsFavoriteAttribute(): bool
    {
        $user = request()->user();

        if (!empty($user)) {
            $favorite = $this->belongsToMany(User::class, 'products_users')->where('user_id', $user->id)->where('product_id', $this->id)->first();

            if (!empty($favorite)) {
                return true;
            }
        }

        if (!empty(getAuthUser())) {
            $favorite = $this->belongsToMany(User::class, 'products_users')->where('user_id', getAuthUser()->id)->where('product_id', $this->id)->first();

            if (!empty($favorite)) {
                return true;
            }
        }

        return false;
    }

    public function getIsAvailableAttribute(): bool
    {
        if ($this->available && $this->count > 0) {
            return true;
        }

        return false;
    }

    public function getIsCartAttribute(): bool
    {
        $user = getAuthUser();
        $userId = $user?->id;
        $token = request()->header('X-Application-Token');

        $cartQuery = Cart::where('product_id', $this->id);

        if (isset($userId)) {
            $cartQuery->where('user_id', $userId);

            $cart = $cartQuery->first();

            if ($cart) {
                return true;
            }
        }

        if (isset($token)) {
            $cartQuery->where('token', $token);

            $cart = $cartQuery->first();

            if ($cart) {
                return true;
            }
        }

        return false;
    }

    public function getDiffDateAttribute(): bool
    {
        $original_date = date_create(Carbon::parse($this->created_at)->format('Y-m-d'));
        $now = date_create(Carbon::now()->format('Y-m-d'));
        $diff = date_diff($original_date, $now);

        if ($diff->format("%a") <= 10) {
            return true;
        }

        return false;
    }

    public function characteristics(): BelongsToMany
    {
        return $this->belongsToMany(Characteristic::class, 'characteristics_products')->withPivot(['value']);
    }

    // -- Scopes --
    public function scopeBrand($query, $brand_id): mixed
    {
        return $query->where('brand_id', $brand_id);
    }

    public function scopeIsAvailable($query): mixed
    {
        return $query->where('available', true)->where('count', '>', 0);
    }

    public function scopePublished($query): mixed
    {
        return $query->where('published', true)->whereNull('deleted_at');
    }

    public function scopeNotChilds($query): mixed
    {
        return $query->whereNull('child_id');
    }

    public function scopeSearchFilter($query, $id, $brand, $category, $published, $article_number, $category_id, $name)
    {
        return $query->when($id ?? null, function ($query, $id) {
            $query->where('id', $id);
        })
            ->whereHas('categories', function ($query) use ($category, $category_id) {
                $query->when($category ?? null, function ($query, $category) use ($category_id) {
                    $query->whereIn('id', $category_id);
                });
            })->when($published ?? null, function ($query, $published) {
                if ($published == 2) {
                    $query->where('published', false);
                } else {
                    $query->where('published', true);
                }
            })->when($name ?? null, function ($query, $name) {
                $query->where('name->ru', 'ilike', '%' . $name . '%');
            })->when($article_number ?? null, function ($query, $article_number) {
                $query->where('article_number', $article_number);
            })->notChilds();
    }
}
