/home/brandsfa/www/app/Repositories/ProductRepository.php
<?php

namespace App\Repositories;

use App\Contracts\Repositories\ProductRepositoryInterface;
use App\Models\Cart;
use App\Models\DealOfTheDay;
use App\Models\FlashDealProduct;
use App\Models\Product;
use App\Models\Tag;
use App\Models\Translation;
use App\Models\Wishlist;
use App\Traits\ProductTrait;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class ProductRepository implements ProductRepositoryInterface
{
    use ProductTrait;

    public function __construct(
        private readonly Product          $product,
        private readonly Translation      $translation,
        private readonly Tag              $tag,
        private readonly Cart             $cart,
        private readonly Wishlist         $wishlist,
        private readonly FlashDealProduct $flashDealProduct,
        private readonly DealOfTheDay     $dealOfTheDay,
    )
    {
    }

    public function addRelatedTags(object $request, object $product): void
    {
        $tagIds = [];
        if ($request->tags != null) {
            $tags = explode(",", $request->tags);
        }
        if (isset($tags)) {
            foreach ($tags as $value) {
                $tag = $this->tag->firstOrNew(
                    ['tag' => trim($value)]
                );
                $tag->save();
                $tagIds[] = $tag->id;
            }
        }
        $product->tags()->sync($tagIds);
    }

    public function add(array $data): string|object
    {
        return $this->product->create($data);
    }

    public function getFirstWhere(array $params, array $relations = []): ?Model
    {
        return $this->product->where($params)->with($relations)->first();
    }

    public function getFirstWhereWithCount(array $params, array $withCount = [], array $relations = []): ?Model
    {
        return $this->product->with($relations)->where($params)->withCount($withCount)->first();
    }

    public function getFirstWhereWithoutGlobalScope(array $params, array $relations = []): ?Model
    {
        return $this->product->withoutGlobalScopes()->where($params)->with($relations)->first();
    }

    public function getFirstWhereActive(array $params, array $relations = []): ?Model
    {
        return $this->product->active()->where($params)->with($relations)->first();
    }

    public function getWebFirstWhereActive(array $params, array $relations = [], array $withCount = []): ?Model
    {
        return $this->product->active()
            ->when(isset($relations['reviews']), function ($query) use ($relations) {
                return $query->with($relations['reviews']);
            })
            ->when(isset($relations['seller.shop']), function ($query) use ($relations) {
                return $query->with($relations['seller.shop']);
            })
            ->when(isset($relations['wishList']), function ($query) use ($relations, $params) {
                return $query->with([$relations['wishList'] => function ($query) use ($params) {
                    return $query->when($params['customer_id'], function ($query) use ($params) {
                        return $query->where('customer_id', $params['customer_id']);
                    });
                }]);
            })
            ->when(isset($relations['compareList']), function ($query) use ($relations, $params) {
                return $query->with([$relations['compareList'] => function ($query) use ($params) {
                    return $query->when($params['customer_id'], function ($query) use ($params) {
                        return $query->where('user_id', $params['customer_id']);
                    });
                }]);
            })
            ->when(isset($params['slug']), function ($query) use ($params) {
                return $query->where('slug', $params['slug']);
            })
            ->when(isset($withCount['orderDetails']), function ($query) use ($withCount) {
                return $query->withCount($withCount['orderDetails']);
            })
            ->when(isset($withCount['wishList']), function ($query) use ($withCount) {
                return $query->withCount($withCount['wishList']);
            })
            ->first();
    }

    public function getList(array $orderBy = [], array $relations = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        // TODO: Implement getList() method.
    }

    public function getListWhere(array $orderBy = [], string $searchValue = null, array $filters = [], array $relations = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        $query = $this->product->with($relations)->when(isset($filters['added_by']) && $this->isAddedByInHouse(addedBy: $filters['added_by']), function ($query) {
            return $query->where(['added_by' => 'admin']);
        })->when(isset($filters['added_by']) && !$this->isAddedByInHouse($filters['added_by']), function ($query) use ($filters) {
            return $query->where(['added_by' => 'seller'])
                ->when(isset($filters['request_status']) && $filters['request_status'] != 'all', function ($query) use ($filters) {
                    $query->where(['request_status' => $filters['request_status']]);
                })
                ->when(isset($filters['seller_id']), function ($query) use ($filters) {
                    return $query->where(['user_id' => $filters['seller_id']]);
                });
        })->when($searchValue, function ($query) use ($filters, $searchValue) {
            $product_ids = $this->translation->where('translationable_type', 'App\Models\Product')
                ->where('key', 'name')
                ->where('value', 'like', "%{$searchValue}%")
                ->pluck('translationable_id');

            return $query->where('name', 'like', "%{$searchValue}%")
                ->orWhere(function ($query) use ($filters) {
                    if (isset($filters['code'])) {
                        $query->where('code', 'like', "%{$filters['code']}%");
                    }
                })
                ->orWhereIn('id', $product_ids);
        })->when(isset($filters['brand_id']) && $filters['brand_id'] != 'all', function ($query) use ($filters) {
            return $query->where(['brand_id' => $filters['brand_id']]);
        })->when(isset($filters['category_id']) && $filters['category_id'] != 'all', function ($query) use ($filters) {
            return $query->where(['category_id' => $filters['category_id']]);
        })->when(isset($filters['sub_category_id']) && $filters['sub_category_id'] != 'all', function ($query) use ($filters) {
            return $query->where(['sub_category_id' => $filters['sub_category_id']]);
        })->when(isset($filters['sub_sub_category_id']) && $filters['sub_sub_category_id'] != 'all', function ($query) use ($filters) {
            return $query->where(['sub_sub_category_id' => $filters['sub_sub_category_id']]);
        })->when(isset($filters['is_shipping_cost_updated']), function ($query) use ($filters) {
            return $query->where(['is_shipping_cost_updated' => $filters['is_shipping_cost_updated']]);
        })->when(isset($filters['status']), function ($query) use ($filters) {
            return $query->where(['status' => $filters['status']]);
        })->when(isset($filters['code']), function ($query) use ($filters) {
            return $query->where(['code' => $filters['code']]);
        })->when(!empty($orderBy), function ($query) use ($orderBy) {
            $query->orderBy(array_key_first($orderBy), array_values($orderBy)[0]);
        });

        $filters += ['searchValue' => $searchValue];
        return $dataLimit == 'all' ? $query->get() : $query->paginate($dataLimit)->appends($filters);
    }

    public function getListWithScope(array $orderBy = [], string $searchValue = null, string $scope = null, array $filters = [], array $whereIn = [], array $whereNotIn = [], array $relations = [], array $withCount = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        $query = $this->product->with($relations)
            ->when(isset($withCount['reviews']), function ($query) use ($withCount) {
                return $query->withCount($withCount['reviews']);
            })
            ->when(isset($scope) && $scope == 'active', function ($query) {
                return $query->active();
            })
            ->when($searchValue, function ($query) use ($searchValue) {
                $product_ids = $this->translation->where('translationable_type', 'App\Models\Product')
                    ->where('key', 'name')
                    ->where('value', 'like', "%{$searchValue}%")
                    ->pluck('translationable_id');
                return $query->where('name', 'like', "%{$searchValue}%")
                    ->orWhereIn('id', $product_ids);
            })
            ->when(isset($filters['search_from']) && $filters['search_from'] == 'pos', function ($query) use ($filters) {
                return $query->where(function ($query) use ($filters) {
                    return $query->where('code', 'like', "%{$filters['keywords']}%")
                        ->orWhere('name', 'like', "%{$filters['keywords']}%");
                });
            })
            ->when(isset($filters['added_by']) && $this->isAddedByInHouse(addedBy: $filters['added_by']), function ($query) {
                return $query->where(['added_by' => 'admin']);
            })->when(isset($filters['added_by']) && !$this->isAddedByInHouse($filters['added_by']), function ($query) use ($filters) {
                return $query->where(['added_by' => 'seller'])
                    ->when(isset($filters['request_status']), function ($query) use ($filters) {
                        $query->where(['request_status' => $filters['request_status']]);
                    })
                    ->when(isset($filters['seller_id']), function ($query) use ($filters) {
                        return $query->where(['user_id' => $filters['seller_id']]);
                    });
            })
            ->when(isset($filters['brand_id']), function ($query) use ($filters) {
                return $query->where(['brand_id' => $filters['brand_id']]);
            })->when(isset($filters['category_id']), function ($query) use ($filters) {
                return $query->where(['category_id' => $filters['category_id']]);
            })->when(isset($filters['sub_category_id']), function ($query) use ($filters) {
                return $query->where(['sub_category_id' => $filters['sub_category_id']]);
            })->when(isset($filters['status']), function ($query) use ($filters) {
                return $query->where(['status' => $filters['status']]);
            })->when(isset($whereIn), function ($query) use ($whereIn) {
                foreach ($whereIn as $key => $whereInIndex) {
                    return $query->whereIn($key, $whereInIndex);
                }
            })
            ->when(isset($filters['sub_sub_category_id']), function ($query) use ($filters) {
                return $query->where(['sub_sub_category_id' => $filters['sub_sub_category_id']]);
            })->when($whereNotIn, function ($query) use ($whereNotIn) {
                foreach ($whereNotIn as $key => $whereNotInIndex) {
                    $query->whereNotIn($key, $whereNotInIndex);
                }
            })->when(!empty($orderBy), function ($query) use ($orderBy) {
                $query->orderBy(array_key_first($orderBy), array_values($orderBy)[0]);
            });

        $filters += ['searchValue' => $searchValue];
        return $dataLimit == 'all' ? $query->get() : $query->paginate($dataLimit)->appends($filters);
    }

    public function getWebListWithScope(array $orderBy = [], string $searchValue = null, string $scope = null, array $filters = [], array $whereHas = [], array $whereIn = [], array $whereNotIn = [], array $relations = [], array $withCount = [], array $withSum = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        $query = $this->product
            ->when(isset($scope) && $scope == 'active', function ($query) {
                return $query->active();
            })
            ->when(isset($filters['added_by']) && $this->isAddedByInHouse(addedBy: $filters['added_by']), function ($query) {
                return $query->where(['added_by' => 'admin']);
            })->when(isset($filters['added_by']) && !$this->isAddedByInHouse($filters['added_by']), function ($query) use ($filters) {
                return $query->where(['added_by' => 'seller']);
            })
            ->when(isset($relations['reviews']), function ($query) use ($relations) {
                return $query->with(isset($relations['reviews']), function ($query) use($relations) {
                    return $query->active();
                });
            })
            ->when(isset($relations['seller.shop']), function ($query) use ($relations) {
                return $query->with($relations['seller.shop']);
            })
            ->when(isset($relations['flashDealProducts.flashDeal']), function ($query) use ($relations) {
                return $query->with($relations['flashDealProducts.flashDeal']);
            })
            ->when(isset($relations['wishList']), function ($query) use ($relations, $filters) {
                return $query->with([$relations['wishList'] => function ($query) use ($filters) {
                    return $query->when($filters['customer_id'], function ($query) use ($filters) {
                        return $query->where('customer_id', $filters['customer_id']);
                    });
                }]);
            })
            ->when(isset($relations['compareList']), function ($query) use ($relations, $filters) {
                return $query->with([$relations['compareList'] => function ($query) use ($filters) {
                    return $query->when($filters['customer_id'], function ($query) use ($filters) {
                        return $query->where('customer_id', $filters['customer_id']);
                    });
                }]);
            })
            ->when(isset($whereHas['reviews']), function ($query) use ($whereHas) {
                return $query;
            })
            ->when(isset($withCount['reviews']), function ($query) use ($withCount) {
                return $query->withCount([$withCount['reviews'] => function ($query) {
                    return $query->active();
                }]);
            })
            ->when($withSum, function ($query) use ($withSum) {
                foreach ($withSum as $sum) {
                    return $query->withSum($sum['relation'], $sum['column'], function ($query) use ($sum) {
                        $query->where($sum['whereColumn'], $sum['whereValue']);
                    });
                }
                return $query->withSum($withSum['orderDetails']);
            })
            ->when(isset($withSum['qty']), function ($query) use ($withSum) {
                return $query->withSum($withSum['qty']);
            })
            ->when($searchValue, function ($query) use ($searchValue) {
                $product_ids = $this->translation->where('translationable_type', 'App\Models\Product')
                    ->where('key', 'name')
                    ->where('value', 'like', "%{$searchValue}%")
                    ->pluck('translationable_id');
                return $query->where('name', 'like', "%{$searchValue}%")->orWhereIn('id', $product_ids);
            })->when(isset($filters['seller_id']), function ($query) use ($filters) {
                return $query->where('user_id', $filters['seller_id']);
            })->when(isset($filters['brand_id']), function ($query) use ($filters) {
                return $query->where(['brand_id' => $filters['brand_id']]);
            })->when(isset($filters['category_id']), function ($query) use ($filters) {
                return $query->where(['category_id' => $filters['category_id']]);
            })->when(isset($filters['sub_category_id']), function ($query) use ($filters) {
                return $query->where(['sub_category_id' => $filters['sub_category_id']]);
            })->when(isset($whereIn), function ($query) use ($whereIn) {
                foreach ($whereIn as $key => $whereInIndex) {
                    return $query->whereIn($key, $whereInIndex);
                }
            })->when(isset($filters['sub_sub_category_id']), function ($query) use ($filters) {
                return $query->where(['sub_sub_category_id' => $filters['sub_sub_category_id']]);
            })->when($whereNotIn, function ($query) use ($whereNotIn) {
                foreach ($whereNotIn as $key => $whereNotInIndex) {
                    $query->whereNotIn($key, $whereNotInIndex);
                }
            })->when(!empty($orderBy), function ($query) use ($orderBy) {
                $query->orderBy(array_key_first($orderBy), array_values($orderBy)[0]);
            });

        $filters += ['searchValue' => $searchValue];
        return $dataLimit == 'all' ? $query->get() : $query->paginate($dataLimit)->appends($filters);
    }

    public function update(string $id, array $data): bool
    {
        return $this->product->where('id', $id)->update($data);
    }
    public function updateByParams(array $params, array $data): bool
    {
        return $this->product->where($params)->update($data);
    }


    public function getListWhereNotIn(array $filters = [], array $whereNotIn = [], array $relations = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        return $this->product->when($whereNotIn, function ($query) use ($whereNotIn) {
            foreach ($whereNotIn as $key => $whereNotInIndex) {
                $query->whereNotIn($key, $whereNotInIndex);
            }
        })->when(isset($filters['user_id']), function ($query) use ($filters) {
            return $query->where(['user_id' => $filters['user_id']]);
        })->when(isset($filters['added_by']), function ($query) use ($filters) {
            return $query->where(['added_by' => $filters['added_by']]);
        })->get();
    }

    public function getTopRatedList(array $filters = [], array $relations = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        return $this->product->with($relations)->where($filters)
            ->with('reviews', function ($query) {
                return $query->whereHas('product', function ($query) {
                    $query->active();
                });
            })
            ->withCount(['reviews' => function ($query){
                return $query->whereNull('delivery_man_id');
            }])
            ->withAvg('rating as ratings_average', 'rating')
            ->orderByDesc('reviews_count')
            ->get();
    }

    public function getTopSellList(array $filters = [], array $relations = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        return $this->product->with($relations)
            ->when(isset($filters['added_by']) && $this->isAddedByInHouse(addedBy: $filters['added_by']), function ($query) {
                return $query->where(['added_by' => 'admin']);
            })->when(isset($filters['added_by']) && !$this->isAddedByInHouse($filters['added_by']), function ($query) use ($filters) {
                return $query->where(['added_by' => 'seller', 'request_status' => $filters['request_status']]);
            })->when(isset($filters['seller_id']), function ($query) use ($filters) {
                return $query->where('user_id', $filters['seller_id']);
            })
            ->when(isset($filters['request_status']), function ($query) use ($filters) {
                return $query->where('request_status', $filters['request_status']);
            })
            ->whereHas('orderDetails', function ($query) {
                $query->where(['delivery_status' => 'delivered']);
            })
            ->withCount('orderDetails')->get()
            ->sortByDesc('order_details_count');
    }

    public function delete(array $params): bool
    {
        return $this->product->where($params)->delete();
    }

    public function getStockLimitListWhere(array $orderBy = [], string $searchValue = null, array $filters = [], array $withCount = [], array $relations = [], int|string $dataLimit = DEFAULT_DATA_LIMIT, int $offset = null): Collection|LengthAwarePaginator
    {
        $stockLimit = getWebConfig(name: 'stock_limit');
        $query = $this->product->with($relations)
            ->withCount($withCount)
            ->when($this->isAddedByInHouse(addedBy: $filters['added_by']), function ($query) {
                return $query->where(['added_by' => 'admin']);
            })
            ->when(!$this->isAddedByInHouse($filters['added_by']), function ($query) use ($filters) {
                return $query->where(['added_by' => 'seller', 'product_type' => 'physical'])
                    ->when(isset($filters['request_status']), function ($query) use ($filters) {
                        return $query->where(['request_status' => $filters['request_status']]);
                    })
                    ->when(isset($filters['seller_id']), function ($query) use ($filters) {
                        return $query->where(['user_id' => $filters['seller_id']]);
                    });
            })
            ->when(isset($filters['product_type']), function ($query) use ($filters) {
                return $query->where(['product_type' => $filters['product_type']]);
            })
            ->when($searchValue, function ($query) use ($searchValue) {
                $product_ids = $this->translation->where('translationable_type', 'App\Models\Product')
                    ->where('key', 'name')
                    ->where('value', 'like', "%{$searchValue}%")
                    ->pluck('translationable_id');

                return $query->where('name', 'like', "%{$searchValue}%")->orWhereIn('id', $product_ids);
            })
            ->where('current_stock', '<', $stockLimit)
            ->when(!empty($orderBy), function ($query) use ($orderBy) {
                return $query->orderBy(array_key_first($orderBy), array_values($orderBy)[0]);
            });

        $filters += ['searchValue' => $searchValue];
        return $dataLimit == 'all' ? $query->get() : $query->paginate($dataLimit)->appends($filters);
    }

    public function getProductIds(array $filters = []): \Illuminate\Support\Collection|array
    {
        return $this->product->when(isset($filters['added_by']), function ($query) use ($filters) {
            return $query->where('added_by', $filters['added_by']);
        })->when(isset($filters['user_id']), function ($query) use ($filters) {
            return $query->where('user_id', $filters['user_id']);
        })->pluck('id');

    }

    public function addArray(array $data): bool
    {
        return DB::table('products')->insert($data);
    }
}