<?php

namespace App\Services;

use App\Models\StockTransaction;
use App\Models\StockAlert;
use App\Repositories\Eloquent\StockLedgerRepository;
use App\Repositories\Eloquent\StockTransactionRepository;
use App\Repositories\Eloquent\StockAlertRepository;
use Illuminate\Support\Facades\DB;

class InventoryService
{
    public function __construct(
        protected StockLedgerRepository $stockLedgerRepository,
        protected StockTransactionRepository $stockTransactionRepository,
        protected StockAlertRepository $stockAlertRepository,
        protected LoggingService $loggingService
    ) {}

    // Stock Ledger Methods
    public function getStockForBranch(int $branchId)
    {
        $this->loggingService->logInfo('Fetching stock for branch', ['branch_id' => $branchId]);
        return $this->stockLedgerRepository->getForBranch($branchId);
    }

    public function getStockForIngredient(int $branchId, int $ingredientId)
    {
        $this->loggingService->logInfo('Fetching stock for ingredient', [
            'branch_id' => $branchId,
            'ingredient_id' => $ingredientId
        ]);
        return $this->stockLedgerRepository->findByBranchAndIngredient($branchId, $ingredientId);
    }

    public function getLowStock(int $branchId)
    {
        $this->loggingService->logInfo('Fetching low stock for branch', ['branch_id' => $branchId]);
        return $this->stockLedgerRepository->getLowStockForBranch($branchId);
    }

    public function getOutOfStock(int $branchId)
    {
        $this->loggingService->logInfo('Fetching out of stock for branch', ['branch_id' => $branchId]);
        return $this->stockLedgerRepository->getOutOfStockForBranch($branchId);
    }

    // Stock Transactions
    public function addStock(int $branchId, int $ingredientId, float $quantity, ?float $unitCost = null, ?int $userId = null, ?string $notes = null, ?string $referenceType = null, ?int $referenceId = null)
    {
        $this->loggingService->logInfo('Adding stock', [
            'branch_id' => $branchId,
            'ingredient_id' => $ingredientId,
            'quantity' => $quantity
        ]);

        return DB::transaction(function () use ($branchId, $ingredientId, $quantity, $unitCost, $userId, $notes, $referenceType, $referenceId) {
            $ledger = $this->stockLedgerRepository->getOrCreate($branchId, $ingredientId);
            $newQuantity = $ledger->quantity + $quantity;

            // Calculate new average cost if unit cost provided
            $newAverageCost = $ledger->average_cost;
            if ($unitCost !== null && $quantity > 0) {
                $totalValue = ($ledger->quantity * $ledger->average_cost) + ($quantity * $unitCost);
                $newAverageCost = $newQuantity > 0 ? $totalValue / $newQuantity : $unitCost;
            }

            $this->stockLedgerRepository->updateQuantity($ledger->id, $newQuantity, $newAverageCost);

            $transaction = $this->stockTransactionRepository->create([
                'branch_id' => $branchId,
                'ingredient_id' => $ingredientId,
                'user_id' => $userId,
                'type' => StockTransaction::TYPE_IN,
                'quantity' => $quantity,
                'unit_cost' => $unitCost,
                'balance_after' => $newQuantity,
                'reference_type' => $referenceType,
                'reference_id' => $referenceId,
                'notes' => $notes,
            ]);

            // Resolve any existing low stock alerts
            $this->checkAndResolveAlerts($branchId, $ingredientId, $newQuantity);

            return $transaction;
        });
    }

    public function deductStock(int $branchId, int $ingredientId, float $quantity, ?int $userId = null, ?string $notes = null, ?string $referenceType = null, ?int $referenceId = null)
    {
        $this->loggingService->logInfo('Deducting stock', [
            'branch_id' => $branchId,
            'ingredient_id' => $ingredientId,
            'quantity' => $quantity
        ]);

        return DB::transaction(function () use ($branchId, $ingredientId, $quantity, $userId, $notes, $referenceType, $referenceId) {
            $ledger = $this->stockLedgerRepository->getOrCreate($branchId, $ingredientId);
            $newQuantity = max(0, $ledger->quantity - $quantity);

            $this->stockLedgerRepository->updateQuantity($ledger->id, $newQuantity);

            $transaction = $this->stockTransactionRepository->create([
                'branch_id' => $branchId,
                'ingredient_id' => $ingredientId,
                'user_id' => $userId,
                'type' => StockTransaction::TYPE_OUT,
                'quantity' => -$quantity,
                'unit_cost' => $ledger->average_cost,
                'balance_after' => $newQuantity,
                'reference_type' => $referenceType,
                'reference_id' => $referenceId,
                'notes' => $notes,
            ]);

            // Check for low stock alerts
            $this->checkAndCreateAlerts($branchId, $ingredientId, $newQuantity);

            return $transaction;
        });
    }

    public function adjustStock(int $branchId, int $ingredientId, float $newQuantity, ?int $userId = null, ?string $notes = null)
    {
        $this->loggingService->logInfo('Adjusting stock', [
            'branch_id' => $branchId,
            'ingredient_id' => $ingredientId,
            'new_quantity' => $newQuantity
        ]);

        return DB::transaction(function () use ($branchId, $ingredientId, $newQuantity, $userId, $notes) {
            $ledger = $this->stockLedgerRepository->getOrCreate($branchId, $ingredientId);
            $adjustment = $newQuantity - $ledger->quantity;

            $this->stockLedgerRepository->updateQuantity($ledger->id, $newQuantity);

            $transaction = $this->stockTransactionRepository->create([
                'branch_id' => $branchId,
                'ingredient_id' => $ingredientId,
                'user_id' => $userId,
                'type' => StockTransaction::TYPE_ADJUSTMENT,
                'quantity' => $adjustment,
                'unit_cost' => $ledger->average_cost,
                'balance_after' => $newQuantity,
                'notes' => $notes ?? 'Stock adjustment',
            ]);

            if ($newQuantity > $ledger->quantity) {
                $this->checkAndResolveAlerts($branchId, $ingredientId, $newQuantity);
            } else {
                $this->checkAndCreateAlerts($branchId, $ingredientId, $newQuantity);
            }

            return $transaction;
        });
    }

    public function recordWaste(int $branchId, int $ingredientId, float $quantity, ?int $userId = null, ?string $notes = null)
    {
        $this->loggingService->logInfo('Recording waste', [
            'branch_id' => $branchId,
            'ingredient_id' => $ingredientId,
            'quantity' => $quantity
        ]);

        return DB::transaction(function () use ($branchId, $ingredientId, $quantity, $userId, $notes) {
            $ledger = $this->stockLedgerRepository->getOrCreate($branchId, $ingredientId);
            $newQuantity = max(0, $ledger->quantity - $quantity);

            $this->stockLedgerRepository->updateQuantity($ledger->id, $newQuantity);

            return $this->stockTransactionRepository->create([
                'branch_id' => $branchId,
                'ingredient_id' => $ingredientId,
                'user_id' => $userId,
                'type' => StockTransaction::TYPE_WASTE,
                'quantity' => -$quantity,
                'unit_cost' => $ledger->average_cost,
                'balance_after' => $newQuantity,
                'notes' => $notes ?? 'Recorded as waste',
            ]);
        });
    }

    public function getTransactions(int $branchId, int $limit = 100)
    {
        $this->loggingService->logInfo('Fetching transactions', ['branch_id' => $branchId]);
        return $this->stockTransactionRepository->getForBranch($branchId, $limit);
    }

    public function getTransactionsForIngredient(int $branchId, int $ingredientId, int $limit = 50)
    {
        $this->loggingService->logInfo('Fetching transactions for ingredient', [
            'branch_id' => $branchId,
            'ingredient_id' => $ingredientId
        ]);
        return $this->stockTransactionRepository->getForIngredient($branchId, $ingredientId, $limit);
    }

    public function getTransactionsByDateRange(int $branchId, $startDate, $endDate)
    {
        $this->loggingService->logInfo('Fetching transactions by date range', [
            'branch_id' => $branchId,
            'start_date' => $startDate,
            'end_date' => $endDate
        ]);
        return $this->stockTransactionRepository->getByDateRange($branchId, $startDate, $endDate);
    }

    // Stock Alerts
    public function getAlerts(int $branchId, bool $unresolvedOnly = true)
    {
        $this->loggingService->logInfo('Fetching stock alerts', ['branch_id' => $branchId]);
        return $this->stockAlertRepository->getForBranch($branchId, $unresolvedOnly);
    }

    public function resolveAlert(int $alertId)
    {
        $this->loggingService->logInfo('Resolving alert', ['alert_id' => $alertId]);
        $alert = $this->stockAlertRepository->find($alertId);
        if ($alert) {
            $alert->resolve();
        }
        return $alert;
    }

    protected function checkAndCreateAlerts(int $branchId, int $ingredientId, float $currentQuantity): void
    {
        $ledger = $this->stockLedgerRepository->findByBranchAndIngredient($branchId, $ingredientId);
        if (!$ledger || !$ledger->ingredient) {
            return;
        }

        $ingredient = $ledger->ingredient;

        if ($currentQuantity <= 0) {
            $this->stockAlertRepository->createOrUpdateAlert(
                $branchId,
                $ingredientId,
                StockAlert::TYPE_OUT_OF_STOCK,
                $currentQuantity,
                0
            );
        } elseif ($currentQuantity <= $ingredient->reorder_level) {
            $this->stockAlertRepository->createOrUpdateAlert(
                $branchId,
                $ingredientId,
                StockAlert::TYPE_LOW_STOCK,
                $currentQuantity,
                $ingredient->reorder_level
            );
        }
    }

    protected function checkAndResolveAlerts(int $branchId, int $ingredientId, float $currentQuantity): void
    {
        $ledger = $this->stockLedgerRepository->findByBranchAndIngredient($branchId, $ingredientId);
        if (!$ledger || !$ledger->ingredient) {
            return;
        }

        $ingredient = $ledger->ingredient;

        if ($currentQuantity > $ingredient->reorder_level) {
            $this->stockAlertRepository->resolveAlertsForIngredient($branchId, $ingredientId);
        }
    }
}
