<?php

namespace App\Models;

use Awobaz\Compoships\Compoships;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Spatie\Activitylog\LogOptions;

class Items extends Model
{
    use Compoships;
    use HasFactory, SoftDeletes;

    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults();
    }

    // protected $with = ['brands', 'categories'];
    protected $table = 'items';

    protected $primaryKey = 'id';

    protected $guarded = ['id'];

    protected $fillable = [
        'number',
        'name',
        'brand_id',
        'category_id',
        'image',
        'description',
        'weight',
        'is_publish',

        // BC data
        'type',
        'unit',
        'display_unit',
        'unit_cost',
        'costing_method',
        'last_direct_cost',
        'unit_price',
        'price_include_vat',
        'sales_blocked',
        'purchasing_blocked',
        'profit_percent',
        'net_weight',
        'gross_weight',
        'unit_volume',
        'net_price',
        'ref_id',

        // purchase,
        'alias_name',
        'group_type',
        'model_type',

        'slug',
    ];

    public function scopeFilter(Builder $query, array $filters)
    {
        $query->when($filters['search'] ?? false, function ($query, $search) {
            return $query
                ->where('number', 'iLIKE', '%'.$search.'%')
                ->orWhere('name', 'iLIKE', '%'.$search.'%');
        });
    }

    public function order()
    {
        return $this->hasMany(SaleOrderDetail::class, 'item_id', 'id');
    }

    public function dimension()
    {
        return $this->hasOne(ItemDimension::class, 'no', 'number');
    }

    public function reference()
    {
        return $this->hasOne(ItemReference::class, 'item_id')
            ->where(function ($query) {
                $query->whereNull('item_variant_id')->orWhere('item_variant_id', 0);
            });
    }

    public function variants()
    {
        return $this->hasMany(ItemVariant::class, 'item_id')
            ->leftJoin('stocks', 'item_variant_id', '=', 'item_variants.id')
            ->select('item_variants.id', 'item_variants.code', 'item_variants.display_name', 'item_variants.is_publish', DB::raw('SUM(quantity) as stock'), 'description', 'item_variants.item_id')
            ->groupBy('item_variants.id', 'item_variants.code', 'description', 'item_variants.item_id', 'item_variants.display_name', 'item_variants.is_publish');
    }

    public function images()
    {
        return $this->hasMany(ItemImage::class, 'item_id')->whereNull('item_variant_id');
    }

    public function itemVariants()
    {
        return $this->hasMany(ItemVariant::class, 'item_id')->with('reference');
    }

    public function publishedItemVariants()
    {
        return $this->hasMany(ItemVariant::class, 'item_id')->with('reference')->where('is_publish', true);
    }

    public function brands()
    {
        return $this->belongsTo(Brand::class, 'brand_id', 'id');
    }

    public function categories()
    {
        return $this->belongsTo(Categories::class, 'categories_id', 'id');
    }

    public function brand()
    {
        return $this->belongsTo(Brand::class, 'brand_id', 'id');
    }

    public function gender()
    {
        return $this->belongsTo(Gender::class, 'gender_id', 'id');
    }

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

    public function discount()
    {
        $user = auth()->user();

        [$location_id, $is_consignment] = Cache::remember(
            'employee_user_'.optional($user)->id,
            60 * 60 * 24,
            function () use ($user) {
                if (! $user) {
                    return [10, false];
                }

                $employee = $user->employee;
                $location = $employee?->location;

                return [
                    $employee?->location_id,
                    (bool) optional($location)->is_consignment,
                ];
            }
        );

        // return $this->hasOne(Discount::class,DB::raw("item_reference.item_id"))
        //     ->leftJoin('discount_items', 'discount_items.discount_id', '=', 'discounts.id')
        //     ->leftJoin('item_reference', 'item_reference.id', '=', 'discount_items.item_reference_id')
        //     ->where('discounts.type', 'discount')
        //     ->where(function ($query) {
        //         $query->whereNull('valid_at')
        //             ->orWhere('valid_at', '<=', now());
        //     })
        //     ->where(function ($query) {
        //         $query->whereNull('expired_at')
        //             ->orWhere('expired_at', '>=', now());
        //     })
        //     ->where(function ($query) use ($location_id, $is_consignment) {
        //         if (!$is_consignment) {
        //             $query->whereNull('discounts.location_id');
        //         }

        //         if ($location_id) {
        //             $query->orWhere('discounts.location_id', $location_id);
        //         }
        //     })
        //     ->orderByDesc('discounts.created_at')
        //     ->select([
        //         'item_reference.item_id',
        //         'discount_items.price',
        //     ]);

        return $this->hasOne(DiscountItem::class, 'item_reference_id', 'id')
            ->leftJoin('discounts', 'discounts.id', '=', 'discount_items.discount_id')
            ->leftJoin('item_reference', 'item_reference.id', '=', 'discount_items.item_reference_id')
            ->where('discounts.type', 'discount')
            ->where(function ($q) {
                $q->whereNull('discounts.valid_at')
                    ->orWhere('discounts.valid_at', '<=', now());
            })
            ->where(function ($q) {
                $q->whereNull('discounts.expired_at')
                    ->orWhere('discounts.expired_at', '>=', now());
            })
            ->where(function ($q) use ($location_id, $is_consignment) {
                if (! $is_consignment) {
                    $q->whereNull('discounts.location_id');
                }

                if ($location_id) {
                    $q->orWhere('discounts.location_id', $location_id);
                }
            })
            ->orderByDesc('discounts.created_at')
            ->select([
                'item_reference.item_id',
                'discount_items.price',
            ]);

    }

    public function price()
    {

        $user = auth()->user();
        $userId = $user ? $user->id : 0;
        [$location_id, $is_consignment] = Cache::remember('employee_user_'.$userId, 60 * 60 * 24, function () use ($user) {

            if ($user == null) {
                return [10, false];
            }

            $employee = @$user->employee;
            $location_id = @$employee->location_id;
            $location = @$employee->location;
            $is_consignment = (bool) @$location->is_consignment;

            return [$location_id, $is_consignment];

        });

        return $this->hasOne(Discount::class, 'id', 'item_reference_id')
            ->leftJoin('discount_items', 'discount_items.discount_id', '=', 'discounts.id')
            ->where('discounts.type', 'price')
            ->where(function ($query) {
                $query->where('valid_at', '<=', now())
                    ->orWhereNull('valid_at');
            })
            ->where(function ($query) {
                $query->where('expired_at', '>', now())
                    ->orWhereNull('expired_at');
            })
            ->where(function ($query) use ($location_id, $is_consignment) {
                if (! $is_consignment) {
                    $query->whereNull('discounts.location_id');
                }

                if ($location_id) {
                    $query->orWhere('discounts.location_id', $location_id);
                }
            })
            ->orderByDesc('discounts.created_at')
            ->select([
                'discount_items.item_reference_id',
                'discount_items.price',
            ]);
    }

    public function stock()
    {
        return $this->hasOne(Stock::class, 'item_id')
            ->leftJoin('locations', 'locations.id', '=', 'location_id')
            ->whereNotNull('locations.display_name')
            ->where('locations.display_name', '<>', '')
            ->where('quantity', '>', '0')
            ->groupBy('item_id')
            ->select('item_id', DB::raw('SUM(quantity) as quantity'));
    }

    public function attributes()
    {
        return $this->hasMany(ItemAttribute::class, 'item_id');
    }

    public function getImageUrlAttribute()
    {
        $image = $this->images->first()->filename ?? null;

        if (str_contains($image, 'http')) {
            return $image;
        }

        return $image ? Storage::disk('wms')->url($image) : null;
    }

    public function getImageUrlsAttribute()
    {
        $images = $this->images ?? [];

        $imgs = [];

        foreach ($images as $img) {
            $image = $img->filename;

            if (str_contains($image, 'http')) {
                $imgs[] = $image;
            } else {
                $imgs[] = $image ? Storage::disk('wms')->url($image) : null;
            }
        }

        return $imgs;
    }

    public function conversion_value()
    {
        $convertion = 1;
        if (($this->display_unit != $this->unit) and ($this->display_unit)) {
            $convertions = DB::select('select to_qty / from_qty as conv
           from item_convertions where from_unit = ? and to_unit =  ?',
                [$this->display_unit, $this->unit]);
            $convertion = max((float) @$convertions[0]->conv, 1);
        }

        return $convertion;
    }

    public function getDisplayPriceAttribute()
    {
        try {
            $convertion = $this->conversion_value();

            $price = @$this->variants->first()->reference->price->price ?? null;
            $price = $price ? $price : $this->net_price;

            return (float) $price * $convertion;
        } catch (\Exception $e) {
            return 0;
        }
    }

    public function getDisplayDiscountPriceAttribute()
    {
        try {
            $convertion = $this->conversion_value();

            $discountPrice = @$this->discount->price ?? 0;

            return (float) $discountPrice * $convertion;
        } catch (\Exception $e) {
            return 0;
        }
    }
}
