<?php

namespace App\Repositories\Ecommerce\Product;

use App\Models\Ecommerce\Product\Inventory\Inventory;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\Ecommerce\Product\Property\Attribute;
use App\Models\Ecommerce\Product\Property\ProductAttributeValue;
use App\Repositories\System\Cache\CacheRepository;
use Closure;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class NovaProductRepository
{
    /**
     * inventory dependsOn
     */
    public static function inventoryDependsOn(string $resource_attribute): Closure
    {
        return function ($field, $request, $formData) {
            $user = $request->user();

            if ($user->isEntrepreneur()) {
                $field->options(Inventory::where(function ($query) use ($user) {
                    $query
                        ->where('channel_id', $user->channel()->id)
                        ->orWhere('shareable', true);
                })->pluck('name', 'id'));
            }

            if ($user->isAdmin()) {
                $field->options(Inventory::pluck('name', 'id'));
            }
        };
    }

    /**
     * Price amount dependsOn
     */
    public static function priceAmountDependsOn($categories = []): Closure
    {
        return function ($field, $request, $formData) use ($categories) {
            if (! $formData->cost_amount) {
                return;
            }

            if (count($categories) > 0) {
                static::setPriceAmount($field, $categories, $formData->cost_amount);

                return;
            }

            if ($formData->categories && is_array($formData->categories)) {
                static::setPriceAmount($field, $formData->categories, $formData->cost_amount);

                return;
            }

            if ($request->isNotFilled('categories')) {
                return;
            }

            // For update request, if there are already categories, tackle it
            $categories = is_string($request->input('categories'))
                ? json_decode($request->input('categories'))
                : $request->input('categories');

            if (! empty($categories)) {
                static::setPriceAmount($field, $categories, $formData->cost_amount);

                return;
            }
        };
    }

    /**
     * Attributes Depends On (create request)
     */
    public static function createRequestAttributesDependsOn($resource): Closure
    {
        return function ($field, $request, $formData) {
            $categories = is_string($formData->categories)
                ? json_decode($formData->categories)
                : $formData->categories;

            if (! $categories) {
                return;
            }

            session(['product_categories' => $categories]);

            $field->fields(static::attributeFields($categories));
        };
    }

    /**
     * Attributes Depends On (update request)
     */
    public static function updateRequestAttributesDependsOn($resource): Closure
    {
        return function ($field, $request, $formData) use ($resource) {
            $categories = is_string($formData->categories)
                ? json_decode($formData->categories)
                : $formData->categories;

            $field->fields(
                static::attributeFieldsWithValues($categories, $resource)
            );
        };
    }

    /**
     * Create request variations depends on
     */
    public static function createRequestVariationsDependsOn(): Closure
    {
        return function ($field, $request, $formData) {
            $categories = is_string($formData->categories)
                ? json_decode($formData->categories)
                : $formData->categories;

            if (! $categories) {
                return;
            }

            $field->show();
        };
    }

    /**
     * Set field's price amount
     *
     * @param  array  $categories
     * @param  int|float|string  $cost_amount
     */
    public static function setPriceAmount($field, array|Collection $categories, $cost_amount): void
    {
        $tax = productTaxForCategory($categories);

        $field->value = ProductRepository::calculatePrice(
            floatval($cost_amount),
            $tax
        );
    }

    /**
     * Attribute fields
     */
    public static function attributeFields($categories): array
    {
        $attribute_categories = DB::table('attribute_category')
            ->whereIntegerInRaw('category_id', $categories)
            ->distinct('attribute_id')
            ->pluck('attribute_id');

        $attributes = Attribute::with('values')
            ->whereIntegerInRaw('id', $attribute_categories)
            ->get()
            ->map(
                fn ($attribute) => [
                    'label' => $attribute->name,
                    'name' => $attribute->slug,
                    'type' => $attribute->type,
                    'required' => $attribute->is_required,
                    'options' => $attribute->values
                        ->map(
                            fn ($value) => [
                                'label' => $value->value,
                                'value' => $value->id,
                            ]
                        )
                        ->toArray(),
                ]
            );

        return $attributes->toArray();
    }

    /**
     * Attribute fields with values
     */
    public static function attributeFieldsWithValues(
        $categories,
        $resource
    ): array {
        if (is_null($categories) || is_null($resource)) {
            return [];
        }

        $attribute_categories = DB::table('attribute_category')
            ->whereIntegerInRaw('category_id', $categories)
            ->distinct('attribute_id')
            ->pluck('attribute_id');

        $product_attributes = DB::table('product_attributes')
            ->where('product_id', $resource->id)
            ->whereIntegerInRaw('attribute_id', $attribute_categories)
            ->get(['id', 'attribute_id']);

        $product_attribute_values = ProductAttributeValue::whereIntegerInRaw(
            column: 'product_attribute_id',
            values: $product_attributes->pluck('id')
        )->get();

        $attributes = Attribute::with('values')
            ->whereIntegerInRaw('id', $attribute_categories)
            ->get()
            ->map(
                fn ($attribute) => [
                    'label' => $attribute->name,
                    'name' => $attribute->slug,
                    'type' => $attribute->type,
                    'required' => false,
                    'default' => $product_attribute_values
                        ->firstWhere(
                            key: 'product_attribute_id',
                            operator: '=',
                            value: $product_attributes->firstWhere(
                                'attribute_id',
                                '=',
                                $attribute->id
                            )?->id
                        )
                        ?->valueForForms(),
                    'options' => $attribute->values
                        ->map(
                            fn ($value) => [
                                'label' => $value->value,
                                'value' => $value->id,
                            ]
                        )
                        ->toArray(),
                ]
            );

        return $attributes->toArray();
    }

    /**
     * Get products
     */
    public static function values(): array
    {
        return CacheRepository::make(
            name: 'cs-nova-models-products',
            value: fn () => Product::where('products.is_visible', true)
                ->where('products.stock', '>', 0)
                ->pluck('name', 'id')
                ->toArray()
        );
    }
}
