<?php

namespace App\Repositories\Eloquent;

use App\Models\Product;
use Illuminate\Database\Eloquent\Model;

class ProductRepository extends BaseRepository
{
    protected function resolveModel(): Model
    {
        return new Product();
    }

    public function all(array $columns = ['*']): \Illuminate\Database\Eloquent\Collection
    {
        return $this->model->with(['category', 'optionSets', 'innerOptionSets'])->get($columns);
    }

    public function findBySlug(string $slug): ?Product
    {
        return $this->model->where('slug', $slug)->first();
    }

    public function getActive()
    {
        return $this->model->active()->with(['category', 'optionSets.items', 'innerOptionSets.items'])->get();
    }

    public function getByCategory(int $categoryId)
    {
        return $this->model->byCategory($categoryId)->active()->get();
    }

    public function getWithRelations(int $id): ?Product
    {
        return $this->model->with([
            'category',
            'optionSets.items',
            'innerOptionSets.items'
        ])->find($id);
    }

    public function getForBranch(int $branchId)
    {
        return $this->model->active()
            ->whereHas('branchProducts', function ($query) use ($branchId) {
                $query->where('branch_id', $branchId)->where('is_available', true);
            })
            ->with([
                'category',
                'optionSets.items'      => fn($q) => $q->where('is_active', true)->orderBy('sort_order'),
                'innerOptionSets.items' => fn($q) => $q->where('is_active', true)->orderBy('sort_order'),
                'branchProducts'        => fn($q) => $q->where('branch_id', $branchId),
                'recipe.productionItems.sourceProduct',
                'recipe.recipeIngredients.ingredient',
            ])
            ->get()
            ->map(function ($product) use ($branchId) {
                $availability          = $this->checkProductAvailability($product, $branchId);
                $product->has_stock    = $availability['available'];
                $product->stock_status = $availability['reason'];   // why it's out of stock
                return $product;
            });
    }

    /**
     * Check if a product can actually be made at this branch right now.
     *
     * Priority order:
     *  1. Has recipe with productionItems / recipeIngredients  → check all of them
     *  2. requires_production = true but no recipe             → check own batch (legacy)
     *  3. Neither                                              → always available
     *
     * Returns ['available' => bool, 'reason' => string|null]
     */
    protected function checkProductAvailability($product, int $branchId): array
    {
        $recipe = $product->recipe;

        if ($recipe) {
            $productionItems  = $recipe->productionItems->whereNull('option_set_item_id');
            $recipeIngredients = $recipe->recipeIngredients->whereNull('option_set_item_id');

            // Check every required production batch
            foreach ($productionItems as $pi) {
                $batchAvailable = \App\Models\ProductionBatch::where('branch_id', $branchId)
                    ->where('product_id', $pi->source_product_id)
                    ->where('status', 'active')
                    ->where(function ($q) {
                        $q->where('remaining_weight_grams', '>', 0)
                          ->orWhere('remaining_scoops', '>', 0);
                    })
                    ->exists();

                if (!$batchAvailable) {
                    $sourceName = $pi->sourceProduct->name ?? "Product #{$pi->source_product_id}";
                    return [
                        'available' => false,
                        'reason'    => "Out of stock: {$sourceName}",
                    ];
                }
            }

            // Check every required ingredient in stock ledger
            foreach ($recipeIngredients as $ri) {
                $stockQty = \App\Models\StockLedger::where('branch_id', $branchId)
                    ->where('ingredient_id', $ri->ingredient_id)
                    ->value('quantity') ?? 0;

                if ($stockQty <= 0) {
                    $ingredientName = $ri->ingredient->name ?? "Ingredient #{$ri->ingredient_id}";
                    return [
                        'available' => false,
                        'reason'    => "Out of stock: {$ingredientName}",
                    ];
                }
            }

            return ['available' => true, 'reason' => null];
        }

        // Legacy: product requires its own production batch (ice cream scoops)
        if ($product->requires_production) {
            $availableScoops = \App\Models\ProductionBatch::where('branch_id', $branchId)
                ->where('product_id', $product->id)
                ->where('status', 'active')
                ->where('remaining_scoops', '>', 0)
                ->sum('remaining_scoops');

            $product->available_scoops = (int) $availableScoops;

            return [
                'available' => $availableScoops > 0,
                'reason'    => $availableScoops > 0 ? null : 'No active production batch',
            ];
        }

        // Non-production products (cones, packaged items etc.) — always available
        return ['available' => true, 'reason' => null];
    }

    public function syncOptionSets(int $productId, array $optionSetIds): void
    {
        $product = $this->find($productId);
        $product->optionSets()->sync($optionSetIds);
    }

    public function syncInnerOptionSets(int $productId, array $innerOptionSetIds): void
    {
        $product = $this->find($productId);
        $product->innerOptionSets()->sync($innerOptionSetIds);
    }
}
