<?php

namespace App\Services;

use App\Models\ProductionBatch;
use App\Models\ScoopSale;
use App\Models\BatchDeduction;
use App\Repositories\Eloquent\ProductionBatchRepository;
use App\Repositories\Eloquent\BatchSizeConfigRepository;
use App\Repositories\Eloquent\ScoopSaleRepository;
use App\Repositories\Eloquent\RecipeRepository;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class ProductionBatchService
{
    public function __construct(
        protected ProductionBatchRepository $batchRepository,
        protected BatchSizeConfigRepository $batchSizeRepository,
        protected ScoopSaleRepository $scoopSaleRepository,
        protected RecipeRepository $recipeRepository,
        protected InventoryService $inventoryService,
        protected LoggingService $loggingService,
        protected ScoopWeightService $scoopWeightService
    ) {}

    // Batch Size Configurations
    public function getBatchSizeConfigs(): Collection
    {
        return $this->batchSizeRepository->getActive();
    }

    public function createBatchSizeConfig(array $data)
    {
        return $this->batchSizeRepository->create($data);
    }

    public function updateBatchSizeConfig(int $id, array $data)
    {
        return $this->batchSizeRepository->update($id, $data);
    }

    public function deleteBatchSizeConfig(int $id): bool
    {
        return $this->batchSizeRepository->delete($id);
    }

    // Production Batches
    public function getAllBatches(array $filters = []): Collection
    {
        return $this->batchRepository->getAll($filters);
    }

    public function getActiveBatches(int $branchId, ?int $productId = null): Collection
    {
        return $this->batchRepository->getActiveBatches($branchId, $productId);
    }

    public function getBatchDetails(int $id): ?ProductionBatch
    {
        return $this->batchRepository->getWithDetails($id);
    }

    public function createBatch(array $data): ProductionBatch
    {
        $this->loggingService->logInfo('Creating production batch', $data);

        return DB::transaction(function () use ($data) {
            // Get recipe for the product
            $recipe = $this->recipeRepository->getWithIngredients($data['recipe_id']);
            if (!$recipe) {
                throw new \InvalidArgumentException("Recipe not found: {$data['recipe_id']}");
            }

            $quantity    = $data['quantity'] ?? 1;
            $totalScoops = $data['total_scoops'] * $quantity;
            $expiryHours = $data['expiry_hours'] ?? 24;
            $expiresAt   = now()->addHours($expiryHours);

            // Create the batch
            $batch = $this->batchRepository->create([
                'branch_id'        => $data['branch_id'],
                'product_id'       => $data['product_id'],
                'recipe_id'        => $data['recipe_id'],
                'produced_by'      => $data['produced_by'],
                'batch_size'       => $data['batch_size'],
                'batch_size_value' => $data['total_scoops'] * $quantity,
                'total_scoops'     => $totalScoops,
                'remaining_scoops' => $totalScoops,
                'sold_scoops'      => 0,
                'wasted_scoops'    => 0,
                'status'           => 'active',
                'produced_at'      => now(),
                'expires_at'       => $expiresAt,
                'notes'            => $data['notes'] ?? null,
            ]);

            // Ingredient multiplier = quantity (1 recipe unit per batch quantity)
            $multiplier = $quantity;

            // Deduct ingredients from inventory and record in batch
            $totalCost = 0;
            foreach ($recipe->recipeIngredients as $recipeIngredient) {
                $quantityNeeded = $recipeIngredient->quantity * $multiplier;
                $ingredient = $recipeIngredient->ingredient;

                // Deduct from inventory
                $this->inventoryService->deductStock(
                    $data['branch_id'],
                    $ingredient->id,
                    $quantityNeeded,
                    $data['produced_by'],
                    "Production batch: {$batch->batch_number}",
                    'production_batch',
                    $batch->id
                );

                // Record ingredient usage in batch
                $unitCost = $ingredient->cost_per_unit;
                $ingredientCost = $quantityNeeded * $unitCost;
                $totalCost += $ingredientCost;

                $batch->ingredients()->create([
                    'ingredient_id' => $ingredient->id,
                    'quantity_used' => $quantityNeeded,
                    'unit' => $recipeIngredient->unit ?? $ingredient->unit,
                    'unit_cost' => $unitCost,
                    'total_cost' => $ingredientCost,
                ]);
            }

            // Calculate cost per scoop
            $costPerScoop = $totalScoops > 0 ? $totalCost / $totalScoops : 0;
            $batch->update(['cost_per_scoop' => $costPerScoop]);

            $this->loggingService->logInfo('Production batch created', [
                'batch_id' => $batch->id,
                'batch_number' => $batch->batch_number,
                'total_scoops' => $totalScoops,
                'cost_per_scoop' => $costPerScoop,
            ]);

            return $batch->fresh(['branch', 'product', 'recipe', 'producer', 'ingredients.ingredient']);
        });
    }

    public function updateBatch(int $id, array $data): ProductionBatch
    {
        $batch = $this->batchRepository->find($id);
        if (!$batch) {
            throw new \InvalidArgumentException("Batch not found: {$id}");
        }

        $this->batchRepository->update($id, $data);

        return $this->batchRepository->getWithDetails($id);
    }

    public function cancelBatch(int $id, string $reason = null): ProductionBatch
    {
        $batch = $this->batchRepository->find($id);
        if (!$batch) {
            throw new \InvalidArgumentException("Batch not found: {$id}");
        }

        if ($batch->status !== 'active') {
            throw new \InvalidArgumentException("Batch is not active and cannot be cancelled");
        }

        $batch->update([
            'status' => 'cancelled',
            'notes' => ($batch->notes ? $batch->notes . "\n" : '') . "Cancelled: " . ($reason ?? 'No reason provided'),
        ]);

        $this->loggingService->logInfo('Production batch cancelled', [
            'batch_id' => $id,
            'reason' => $reason,
        ]);

        return $batch->fresh();
    }

    public function recordBatchWaste(int $id, int $scoopCount, string $reason = null): ProductionBatch
    {
        $batch = $this->batchRepository->find($id);
        if (!$batch) {
            throw new \InvalidArgumentException("Batch not found: {$id}");
        }

        $batch->recordWaste($scoopCount, $reason);

        $this->loggingService->logInfo('Batch waste recorded', [
            'batch_id' => $id,
            'scoop_count' => $scoopCount,
            'reason' => $reason,
        ]);

        return $batch->fresh();
    }

    public function markExpiredBatches(): int
    {
        $expiredBatches = $this->batchRepository->getExpiredBatches();
        $count = 0;

        foreach ($expiredBatches as $batch) {
            $batch->markAsExpired();
            $count++;

            $this->loggingService->logInfo('Batch marked as expired', [
                'batch_id' => $batch->id,
                'remaining_scoops' => $batch->remaining_scoops,
            ]);
        }

        return $count;
    }

    // Scoop Sales
    public function recordScoopSale(
        int $productionBatchId,
        int $orderId,
        int $orderItemId,
        int $productId,
        int $branchId,
        int $scoopCount,
        float $salePrice
    ): ScoopSale {
        return DB::transaction(function () use ($productionBatchId, $orderId, $orderItemId, $productId, $branchId, $scoopCount, $salePrice) {
            $batch = $this->batchRepository->find($productionBatchId);
            if (!$batch) {
                throw new \InvalidArgumentException("Production batch not found: {$productionBatchId}");
            }

            if (!$batch->deductScoops($scoopCount)) {
                throw new \InvalidArgumentException("Insufficient scoops in batch. Available: {$batch->remaining_scoops}");
            }

            $scoopSale = $this->scoopSaleRepository->create([
                'production_batch_id' => $productionBatchId,
                'order_id' => $orderId,
                'order_item_id' => $orderItemId,
                'product_id' => $productId,
                'branch_id' => $branchId,
                'scoop_count' => $scoopCount,
                'sale_price' => $salePrice,
                'sold_at' => now(),
            ]);

            $this->loggingService->logInfo('Scoop sale recorded', [
                'batch_id' => $productionBatchId,
                'order_id' => $orderId,
                'scoop_count' => $scoopCount,
            ]);

            return $scoopSale;
        });
    }

    /**
     * Record weight deduction from batch (new weight-based system)
     */
    public function recordWeightDeduction(
        int $productionBatchId,
        int $orderId,
        int $orderItemId,
        float $weightGrams,
        ?string $notes = null
    ): BatchDeduction {
        return DB::transaction(function () use ($productionBatchId, $orderId, $orderItemId, $weightGrams, $notes) {
            $batch = $this->batchRepository->find($productionBatchId);
            if (!$batch) {
                throw new \InvalidArgumentException("Production batch not found: {$productionBatchId}");
            }

            // Deduct weight from batch
            if (!$batch->deductWeight($weightGrams)) {
                throw new \InvalidArgumentException("Insufficient weight in batch. Available: {$batch->remaining_weight_grams}g");
            }

            // Calculate cost based on batch cost per gram
            $costAmount = null;
            if ($batch->total_weight_grams > 0) {
                $totalCost = $batch->calculateTotalCost();
                $costPerGram = $totalCost / $batch->total_weight_grams;
                $costAmount = $costPerGram * $weightGrams;
            }

            // Create batch deduction record for audit trail
            $batchDeduction = BatchDeduction::create([
                'production_batch_id' => $productionBatchId,
                'order_id' => $orderId,
                'order_item_id' => $orderItemId,
                'weight_grams' => $weightGrams,
                'scoop_count' => null, // Weight-based, no scoop count
                'cost_amount' => $costAmount,
                'notes' => $notes,
            ]);

            $this->loggingService->logInfo('Weight deduction recorded', [
                'batch_id' => $productionBatchId,
                'order_id' => $orderId,
                'weight_grams' => $weightGrams,
                'remaining_weight' => $batch->remaining_weight_grams,
            ]);

            return $batchDeduction;
        });
    }

    public function findAvailableBatchForProduct(int $branchId, int $productId): ?ProductionBatch
    {
        return $this->batchRepository->findActiveBatchForProduct($branchId, $productId);
    }

    // Reports
    public function getDailyScoopSummary(int $branchId, string $date): Collection
    {
        return $this->batchRepository->getDailyScoopSummary($branchId, $date);
    }

    public function getDailySalesByProduct(int $branchId, string $date): Collection
    {
        return $this->scoopSaleRepository->getDailySalesByProduct($branchId, $date);
    }

    public function getSalesReport(int $branchId, string $startDate, string $endDate): Collection
    {
        return $this->scoopSaleRepository->getSalesBetweenDates($branchId, $startDate, $endDate);
    }

    public function getTotalScoopsSold(int $branchId, ?string $date = null): int
    {
        return $this->scoopSaleRepository->getTotalScoopsSold($branchId, $date);
    }

    /**
     * Record scoop sale with non-linear weight calculation
     * Supports multiple flavours in a single order
     *
     * @param array $saleData Contains: order_id, order_item_id, branch_id, flavours, sale_price
     * @return array Array of ScoopSale records created (one per flavour)
     */
    public function recordScoopSaleWithWeights(array $saleData): array
    {
        return DB::transaction(function () use ($saleData) {
            // Calculate weight distribution
            $weightDistribution = $this->scoopWeightService->calculateWeightDistribution($saleData['flavours']);

            $scoopSales = [];

            foreach ($weightDistribution['flavours'] as $flavourData) {
                // Find available batch for this product (flavour)
                $batch = $this->findAvailableBatchForProduct(
                    $saleData['branch_id'],
                    $flavourData['product_id']
                );

                if (!$batch) {
                    $this->loggingService->logWarning('No available batch for product', [
                        'product_id' => $flavourData['product_id'],
                        'branch_id' => $saleData['branch_id'],
                    ]);
                    continue;
                }

                // Deduct scoops from batch
                if (!$batch->deductScoops($flavourData['scoop_count'])) {
                    throw new \InvalidArgumentException(
                        "Insufficient scoops for product {$flavourData['product_id']}. Available: {$batch->remaining_scoops}"
                    );
                }

                // Record scoop sale with weight information
                $scoopSale = $this->scoopSaleRepository->create([
                    'production_batch_id' => $batch->id,
                    'order_id' => $saleData['order_id'],
                    'order_item_id' => $saleData['order_item_id'],
                    'product_id' => $flavourData['product_id'],
                    'branch_id' => $saleData['branch_id'],
                    'scoop_count' => $flavourData['scoop_count'],
                    'scoop_weight_grams' => $flavourData['weight_grams'],
                    'total_scoops_in_order' => $weightDistribution['total_scoops'],
                    'weight_calculation_notes' => json_encode([
                        'total_scoops' => $weightDistribution['total_scoops'],
                        'weight_per_scoop' => $weightDistribution['weight_per_scoop_grams'],
                        'calculation_method' => $weightDistribution['calculation_method'],
                    ]),
                    'sale_price' => $saleData['sale_price'] ?? 0,
                    'sold_at' => now(),
                ]);

                $scoopSales[] = $scoopSale;

                $this->loggingService->logInfo('Weight-based scoop sale recorded', [
                    'batch_id' => $batch->id,
                    'product_id' => $flavourData['product_id'],
                    'scoop_count' => $flavourData['scoop_count'],
                    'weight_grams' => $flavourData['weight_grams'],
                    'total_scoops' => $weightDistribution['total_scoops'],
                ]);
            }

            return $scoopSales;
        });
    }

    /**
     * Get scoop weight configurations
     */
    public function getScoopWeightConfigs(): Collection
    {
        return $this->scoopWeightService->getAllConfigurations();
    }

    /**
     * Update scoop weight configuration
     */
    public function updateScoopWeightConfig(int $scoopCount, array $data)
    {
        return $this->scoopWeightService->updateConfiguration($scoopCount, $data);
    }

    /**
     * Get weight efficiency report
     */
    public function getWeightEfficiencyReport(): array
    {
        return $this->scoopWeightService->getWeightEfficiencyReport();
    }
}
