<?php

namespace App\Services;

use App\Models\InventoryMovement;
use App\Models\Item;
use App\Models\MoveOrderItem;

class InventoryService
{
    /**
     * Dynamically compute current SOH for an item
     * based on all inventory movements (IN - OUT)
     */
    public function computeSOH(int $itemId, int $farmId): float
    {
        return (float) InventoryMovement::where('item_id', $itemId)
            ->where('farm_id', $farmId)
            ->selectRaw("SUM(CASE WHEN type = 'IN' THEN quantity ELSE -quantity END) as soh")
            ->value('soh') ?? 0;
    }

    /**
     * ==================================================
     * HELPER
     * Snapshot total SOH across ALL farms for an item.
     * Updates the items.soh column for quick reference.
     * ==================================================
     */
    public function syncItemSOH(int $itemId): void
    {
        $totalSOH = (float) InventoryMovement::where('item_id', $itemId)
            ->selectRaw("SUM(CASE WHEN type = 'IN' THEN quantity ELSE -quantity END) as soh")
            ->value('soh') ?? 0;

        Item::where('id', $itemId)->update(['soh' => $totalSOH]);
    }

    /**
     * ==================================================
     * RECEIVING - CREATE
     * ==================================================
     */
    public function in(array $data)
    {
        $item = Item::findOrFail($data['item_id']);

        $newBalance = $this->computeSOH($item->id, $data['farm_id']) + $data['quantity'];

        InventoryMovement::create([
            'farm_id'        => $data['farm_id'],
            'item_id'        => $item->id,
            'type'           => 'IN',
            'quantity'       => $data['quantity'],
            'unit_cost'      => $data['unit_cost'],
            'total_cost'     => $data['quantity'] * $data['unit_cost'],
            'balance_after'  => $newBalance,
            'reference_type' => $data['reference_type'],
            'reference_id'   => $data['reference_id'],
            'remarks'        => $data['remarks'] ?? null,
            'user_id'        => $data['user_id'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * RECEIVING - UPDATE
     * Updates the existing IN movement record directly.
     * Recalculates balance_after based on updated quantity/cost.
     * ==================================================
     */
    public function inUpdate(array $data)
    {
        $movement = InventoryMovement::where('reference_type', $data['reference_type'])
            ->where('reference_id', $data['reference_id'])
            ->where('item_id', $data['item_id'])
            ->where('farm_id', $data['farm_id'])
            ->where('type', 'IN')
            ->firstOrFail();

        // Recompute SOH excluding this movement, then re-add the new quantity
        $sohExcluding = $this->computeSOHExcluding($data['item_id'], $data['farm_id'], $movement->id);
        $newBalance   = $sohExcluding + $data['quantity'];

        $movement->update([
            'quantity'      => $data['quantity'],
            'unit_cost'     => $data['unit_cost'],
            'total_cost'    => $data['quantity'] * $data['unit_cost'],
            'balance_after' => $newBalance,
            'remarks'       => $data['remarks'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * MOVE ORDER RELEASE - CREATE
     * ==================================================
     */
    public function outWarehouse(array $data)
    {
        $item = Item::findOrFail($data['item_id']);

        $newBalance = $this->computeSOH($item->id, $data['farm_id']) - $data['quantity'];

        InventoryMovement::create([
            'farm_id'        => $data['farm_id'],
            'item_id'        => $item->id,
            'type'           => 'OUT',
            'quantity'       => $data['quantity'],
            'unit_cost'      => 0,
            'total_cost'     => 0,
            'balance_after'  => $newBalance,
            'reference_type' => 'move_order',
            'reference_id'   => $data['reference_id'],
            'remarks'        => $data['remarks'] ?? null,
            'user_id'        => $data['user_id'] ?? null,
        ]);

        // $moveOrderItem = MoveOrderItem::findOrFail($data['move_order_item_id']);

        // $moveOrderItem->update([
        //     'released_qty'  => $data['quantity'],
        //     'remaining_qty' => $data['quantity'],
        // ]);

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * MOVE ORDER RELEASE - UPDATE
     * Updates the existing OUT movement record directly.
     * ==================================================
     */
    public function outWarehouseUpdate(array $data)
    {
        $movement = InventoryMovement::where('reference_type', 'move_order')
            ->where('reference_id', $data['reference_id'])
            ->where('item_id', $data['item_id'])
            ->where('farm_id', $data['farm_id'])
            ->where('type', 'OUT')
            ->firstOrFail();

        $sohExcluding = $this->computeSOHExcluding($data['item_id'], $data['farm_id'], $movement->id);
        $newBalance   = $sohExcluding - $data['quantity'];

        $movement->update([
            'quantity'      => $data['quantity'],
            'balance_after' => $newBalance,
            'remarks'       => $data['remarks'] ?? null,
        ]);

        $moveOrderItem = MoveOrderItem::findOrFail($data['move_order_item_id']);

        $moveOrderItem->update([
            'released_qty'  => $data['quantity'],
            'remaining_qty' => $data['quantity'],
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * MISC ISSUE - CREATE
     * General stock-out from farm inventory (not move order related)
     * ==================================================
     */
    public function outMiscIssue(array $data)
    {
        $item = Item::findOrFail($data['item_id']);

        $newBalance = $this->computeSOH($item->id, $data['farm_id']) - $data['quantity'];

        InventoryMovement::create([
            'farm_id'        => $data['farm_id'],
            'item_id'        => $item->id,
            'type'           => 'OUT',
            'quantity'       => $data['quantity'],
            'unit_cost'      => 0,
            'total_cost'     => 0,
            'balance_after'  => $newBalance,
            'reference_type' => 'misc_issue',
            'reference_id'   => $data['reference_id'],
            'remarks'        => $data['remarks'] ?? null,
            'user_id'        => $data['user_id'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * MISC ISSUE - UPDATE
     * ==================================================
     */
    public function outMiscIssueUpdate(array $data): void
    {
        $movement = InventoryMovement::where('reference_type', 'misc_issue')
            ->where('reference_id', $data['reference_id'])
            ->where('item_id', $data['item_id'])
            ->where('farm_id', $data['farm_id'])
            ->where('type', 'OUT')
            ->firstOrFail();

        $sohExcluding = $this->computeSOHExcluding($data['item_id'], $data['farm_id'], $movement->id);
        $newBalance   = $sohExcluding - $data['quantity'];

        $movement->update([
            'quantity'      => $data['quantity'],
            'balance_after' => $newBalance,
            'remarks'       => $data['remarks'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * FEED CONSUMPTION - CREATE
     * ==================================================
     */
    public function consumeAllocation(array $data)
    {
        $moveOrderItem = MoveOrderItem::findOrFail($data['move_order_item_id']);

        $moveOrderItem->update([
            'remaining_qty' => $moveOrderItem->remaining_qty - $data['quantity'],
        ]);

        $newBalance = $this->computeSOH($moveOrderItem->item_id, $data['farm_id']) - $data['quantity'];

        InventoryMovement::create([
            'farm_id'        => $data['farm_id'],
            'item_id'        => $moveOrderItem->item_id,
            'type'           => 'OUT',
            'quantity'       => $data['quantity'],
            'unit_cost'      => 0,
            'total_cost'     => 0,
            'balance_after'  => $newBalance,
            'reference_type' => 'feed_consumption',
            'reference_id'   => $data['reference_id'],
            'remarks'        => $data['remarks'] ?? null,
            'user_id'        => $data['user_id'] ?? null,
        ]);

        $this->syncItemSOH($moveOrderItem->item_id);
    }

    /**
     * ==================================================
     * MOVE ORDER RELEASE - REVERSE (item removed on update)
     * ==================================================
     */
    public function reverseOutWarehouse(array $data): void
    {
        InventoryMovement::where('reference_type', 'move_order')
            ->where('reference_id', $data['reference_id'])
            ->where('item_id', $data['item_id'])
            ->where('farm_id', $data['farm_id'])
            ->where('type', 'OUT')
            ->delete();

        $this->syncItemSOH($data['item_id']);
    }

    /**
     * ==================================================
     * HELPER
     * Compute SOH excluding a specific movement record.
     * Used to cleanly recalculate balance_after on update.
     * ==================================================
     */
    public function computeSOHExcluding(int $itemId, int $farmId, int $excludeMovementId): float
    {
        return (float) InventoryMovement::where('item_id', $itemId)
            ->where('farm_id', $farmId)
            ->where('id', '!=', $excludeMovementId)
            ->selectRaw("SUM(CASE WHEN type = 'IN' THEN quantity ELSE -quantity END) as soh")
            ->value('soh') ?? 0;
    }

    // ---------------------------------------------------------------------------
    // TRANSFER OUT — CREATE
    // Deducts stock from the SOURCE farm immediately when Transfer Out is created.
    // Destination farm SOH is untouched until a Transfer In is submitted.
    // ---------------------------------------------------------------------------
    public function outTransfer(array $data): void
    {
        $item = Item::findOrFail($data['item_id']);

        $newBalance = $this->computeSOH($item->id, $data['farm_id']) - $data['quantity'];

        InventoryMovement::create([
            'farm_id'        => $data['farm_id'],             // SOURCE farm
            'item_id'        => $item->id,
            'type'           => 'OUT',
            'quantity'       => $data['quantity'],
            'unit_cost'      => 0,
            'total_cost'     => 0,
            'balance_after'  => $newBalance,
            'reference_type' => 'transfer_out',         // points to stock_transfer_outs
            'reference_id'   => $data['reference_id'],        // stock_transfer_outs.id
            'remarks'        => $data['remarks'] ?? null,
            'user_id'        => $data['user_id'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    // ---------------------------------------------------------------------------
    // TRANSFER OUT — UPDATE
    // Adjusts the existing OUT movement when a pending Transfer Out is edited.
    // Only callable while Transfer Out status = 'pending'.
    // ---------------------------------------------------------------------------
    public function outTransferUpdate(array $data): void
    {
        $movement = InventoryMovement::where('reference_type', 'transfer_out')
            ->where('reference_id', $data['reference_id'])
            ->where('item_id', $data['item_id'])
            ->where('type', 'OUT')
            ->firstOrFail();

        $sohExcluding = $this->computeSOHExcluding($data['item_id'], $movement->farm_id, $movement->id);
        $newBalance   = $sohExcluding - $data['quantity'];

        $movement->update([
            'quantity'      => $data['quantity'],
            'balance_after' => $newBalance,
            'remarks'       => $data['remarks'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    // ---------------------------------------------------------------------------
    // TRANSFER IN — CREATE
    // Credits stock to the DESTINATION farm when Transfer In is submitted.
    // Each Transfer In (full or partial) creates its own IN movement.
    // reference_id points to stock_transfer_ins.id — not the Transfer Out —
    // so each partial receiving is individually traceable.
    // ---------------------------------------------------------------------------
    public function inTransfer(array $data): void
    {
        $item = Item::findOrFail($data['item_id']);

        $newBalance = $this->computeSOH($item->id, $data['destination_farm_id']) + $data['quantity'];

        InventoryMovement::create([
            'farm_id'        => $data['destination_farm_id'],  // DESTINATION farm
            'item_id'        => $item->id,
            'type'           => 'IN',
            'quantity'       => $data['quantity'],
            'unit_cost'      => $data['unit_cost'] ?? 0,
            'total_cost'     => $data['quantity'] * ($data['unit_cost'] ?? 0),
            'balance_after'  => $newBalance,
            'reference_type' => 'transfer_in',           // points to stock_transfer_ins
            'reference_id'   => $data['reference_id'],         // stock_transfer_ins.id
            'remarks'        => $data['remarks'] ?? null,
            'user_id'        => $data['user_id'] ?? null,
        ]);

        $this->syncItemSOH($data['item_id']);
    }

    // ---------------------------------------------------------------------------
    // TRANSFER OUT — REVERSE (cancel)
    // Restores source farm SOH by deleting the OUT movement.
    // Only callable while Transfer Out status = 'pending' (no Transfer Ins yet).
    // ---------------------------------------------------------------------------
    public function reverseOutTransfer(array $data): void
    {
        InventoryMovement::where('reference_type', 'transfer_out')
            ->where('reference_id', $data['reference_id'])    // stock_transfer_outs.id
            ->where('item_id', $data['item_id'])
            ->where('farm_id', $data['farm_id'])
            ->where('type', 'OUT')
            ->delete();

        $this->syncItemSOH($data['item_id']);
    }
}
