<?php
/**
 * Raktári Kiszedési Lista Generátor Osztály
 *
 * Funkció: Rendelések teljesítéséhez szükséges picking list generálása
 * Használat: require_once, majd PickingListGenerator::generate() hívás
 */

class PickingListGenerator {

	private $conn;
	private $warehouseConfig;
	private $primaryWarehouse;

	const PRIMARY_WAREHOUSE_CODE = 'FŐ';
	const FOIL_WAREHOUSE_LABEL = 'Fóliás raktár';
	const CL_SET_PREFIX = 'CL';

	/**
	 * Konstruktor
	 */
	public function __construct($conn) {
		$this->conn = $conn;
		$this->primaryWarehouse = self::PRIMARY_WAREHOUSE_CODE;
		$this->warehouseConfig = $this->loadWarehouseStructure();
	}

	/**
	 * Fő generáló metódus
	 */
	public static function generate($conn, $inputData, $coderclass = null) {
		$generator = new self($conn);
		return $generator->processPickingList($inputData, $coderclass);
	}

	/**
	 * Picking list feldolgozás
	 */
	private function processPickingList($inputData, $coderclass) {
		$orders = [];
		$reason = '';
		$orderDate = '';

		// Input típus meghatározása
		if (isset($inputData['manual']) && is_array($inputData['manual'])) {
			$orders = $this->buildManualOrders($inputData['manual']);
		} else {
			if ($coderclass === null) {
				return ['result' => 'error', 'message' => 'CoderClass szükséges reason alapú lekérdezéshez'];
			}

			$encodedData = htmlspecialchars($inputData['reason'] ?? '');
			$decodedData = $coderclass->decode($encodedData, 'SZ4TUN4');
			$dataParts = explode('|', $decodedData);

			if (count($dataParts) != 2) {
				return ['result' => 'error', 'message' => 'Érvénytelen kiszedési lista azonosító'];
			}

			$reason = $dataParts[0];
			$orderDate = $dataParts[1];

			$orders = $this->loadOrders($reason, $orderDate);
		}

		// Készletek betöltése
		$boxStock = $this->loadBoxStock();
		$foilStock = $this->loadFoilStock();

		// Picking listák generálása
		$allPickingLists = [];

		foreach ($orders as $key => $orderData) {

			$localBoxStock = $boxStock;
			$localFoilStock = $foilStock;

			$orderPrimaryWarehouse = $orderData['primary_warehouse'] ?? $this->primaryWarehouse;

			$this->prioritizeStock($localBoxStock, $orderPrimaryWarehouse);
			$this->prioritizeFoilStock($localFoilStock, $orderPrimaryWarehouse);

			$pickingList = $this->generatePickingList($orderData, $localBoxStock, $localFoilStock);

			$this->formatPositionForSort($pickingList);
			$this->sortPickingList($pickingList);

			$icon = $this->determineIcon($pickingList);
			if (!empty($icon)) {
				$icon = $orderData['overal_order_id'] . ' ' . $icon;
			}

			$allPickingLists[] = [
				'overal_order_id' => $orderData['overal_order_id'],
				'order_id' => $orderData['order_id'],
				'order_name' => $orderData['order_name'],
				'order_mail' => $orderData['order_mail'],
				'receipt_method' => $orderData['receipt_method'],
				'order_note' => $orderData['order_note'],
				'picking_list' => $pickingList
			];
		}

		$this->sortOrders($allPickingLists);

		return [
			'result' => 'ok',
			'reason' => $reason,
			'date' => $orderDate,
			'all_picking_lists' => $allPickingLists
		];
	}

	// ========================================================================
	// ADATBETÖLTŐ METÓDUSOK
	// ========================================================================

	private function loadWarehouseStructure() {
		$config = [];
		$sql = "SELECT warehouse_id, location, name, code FROM warehouse_structure WHERE status=1";
		$result = $this->conn->query($sql);

		if ($result) {
			while ($row = $result->fetch_assoc()) {
				$config[intval($row['warehouse_id'])] = [
					'location' => $row['location'],
					'name' => $row['name'],
					'code' => $row['code']
				];
			}
		}

		return $config;
	}

	private function buildManualOrders($manualData) {
		$manualItems = [];

		foreach ($manualData as $entry) {
			$itemId = $entry['item_id'] ?? '';
			$amount = intval($entry['amount'] ?? 0);
			$primarySource = intval($entry['primary_source'] ?? 0);
			$amountType = intval($entry['amount_type'] ?? 0);

			if (empty($itemId) || $amount <= 0) continue;

			$key = $itemId . '_' . $primarySource . '_' . $amountType;

			if (!isset($manualItems[$key])) {
				$manualItems[$key] = [
					'item_id' => $itemId,
					'amount' => 0,
					'primary_source' => $primarySource,
					'amount_type' => $amountType
				];
			}

			$manualItems[$key]['amount'] += $amount;
		}

		$orders = [];
		$orders['manual'] = [
			'overal_order_id' => 'Manuális kiszedés',
			'order_id' => '',
			'order_name' => 'Manuális kiszedés',
			'order_mail' => '',
			'receipt_method' => '',
			'order_note' => '',
			'items' => [],
			'totalneed' => 0,
			'primary_warehouse' => $this->primaryWarehouse
		];

		foreach ($manualItems as $item) {
			$orders['manual']['items'][] = [
				'item_id' => $item['item_id'],
				'need' => $item['amount'],
				'taken_out' => 0,
				'ordered' => $item['amount'],
				'amount_type' => $item['amount_type'],
				'primary_source' => $item['primary_source']
			];
			$orders['manual']['totalneed'] += $item['amount'];
		}

		return $orders;
	}

	private function loadOrders($reason, $orderDate) {
		$orders = [];

		$sql = "SELECT
			item_id,
			SUM(amount) AS ordered,
			SUM(taken_out) AS taken_out,
			SUM(amount) - SUM(taken_out) AS need,
			IFNULL(order_id, '') AS order_id,
			IFNULL(order_name, '') AS order_name,
			IFNULL(order_mail, '') AS order_mail,
			MAX(reason) AS reason,
			primary_source,
			primary_warehouse,
			MAX(amount_type) AS amount_type,
			GROUP_CONCAT(DISTINCT IFNULL(note, '') SEPARATOR ' ') AS note,
			GROUP_CONCAT(DISTINCT IFNULL(receipt_method, '') SEPARATOR ' ') AS receipt_method
		FROM warehouse_reservation
		WHERE reason = ?
			AND DATE(FROM_UNIXTIME(date_create)) = ?
			AND is_active = 1
		GROUP BY item_id, order_id, order_name, order_mail, primary_source, amount_type, primary_warehouse
		ORDER BY amount_type DESC, item_id";

		$stmt = $this->conn->prepare($sql);
		$stmt->bind_param("ss", $reason, $orderDate);
		$stmt->execute();
		$result = $stmt->get_result();

		while ($row = $result->fetch_assoc()) {
			$hasOrder = !empty($row['order_id']) || !empty($row['order_name']) || !empty($row['order_mail']);

			if ($hasOrder) {
				$key = $row['order_id'] . '~' . $row['order_name'] . '~' . $row['order_mail'];
				$label = $row['order_name'] . ' - (' . $row['order_id'] . ')';
			} else {
				$key = 'maradek';
				$label = '!&maradek&! ';
			}

			if (!isset($orders[$key])) {
				$orders[$key] = [
					'overal_order_id' => $label,
					'order_id' => strval($row['order_id']),
					'order_name' => $row['order_name'],
					'order_mail' => $row['order_mail'],
					'receipt_method' => '',
					'order_note' => '',
					'items' => [],
					'totalneed' => 0,
					'primary_warehouse' => $row['primary_warehouse'] ?? $this->primaryWarehouse
				];
			}

			if (!empty($row['receipt_method'])) {
				if (empty($orders[$key]['receipt_method']) || strpos($orders[$key]['receipt_method'], $row['receipt_method']) === false) {
					$orders[$key]['receipt_method'] = trim($orders[$key]['receipt_method'] . ' ' . $row['receipt_method']);
				}
			}

			if (!empty($row['note'])) {
				if (empty($orders[$key]['order_note']) || strpos($orders[$key]['order_note'], $row['note']) === false) {
					$orders[$key]['order_note'] = trim($orders[$key]['order_note'] . ' ' . $row['note']);
				}
			}

			$need = intval($row['need']);
			$orders[$key]['items'][] = [
				'item_id' => $row['item_id'],
				'need' => $need,
				'taken_out' => intval($row['taken_out']),
				'ordered' => intval($row['ordered']),
				'amount_type' => intval($row['amount_type']),
				'primary_source' => intval($row['primary_source'])
			];
			$orders[$key]['totalneed'] += max(0, $need);
		}

		$stmt->close();
		return $orders;
	}

	private function loadBoxStock() {
		$stock = [];

		$sql = "SELECT item_id, wid, position AS pos, amount, warehouse_id
			FROM warehouse
			WHERE amount > 0
			ORDER BY item_id,
				CAST(SUBSTRING_INDEX(position, ':', 1) AS UNSIGNED) DESC,
				CAST(SUBSTRING_INDEX(position, ':', -1) AS UNSIGNED) ASC,
				amount ASC";

		$result = $this->conn->query($sql);

		if ($result) {
			while ($row = $result->fetch_assoc()) {
				$stock[$row['item_id']][] = [
					'wid' => intval($row['wid']),
					'pos' => $row['pos'],
					'amount' => intval($row['amount']),
					'warehouse_id' => intval($row['warehouse_id'])
				];
			}
		}

		return $stock;
	}

	private function loadFoilStock() {
		$stock = [];

		$sql = "SELECT item_id, wid, place AS pos, right_db, left_db
			FROM warehouse_foil
			WHERE (right_db > 0 OR left_db > 0)
				AND is_active = 1
			ORDER BY item_id, right_db ASC";

		$result = $this->conn->query($sql);

		if ($result) {
			while ($row = $result->fetch_assoc()) {
				$stock[$row['item_id']][] = [
					'wid' => 'F' . intval($row['wid']),
					'pos' => $row['pos'],
					'amount' => min(intval($row['right_db']), intval($row['left_db'])),
					'warehouse_id' => 'FOIL',
					'right_db' => intval($row['right_db']),
					'left_db' => intval($row['left_db'])
				];
			}
		}

		return $stock;
	}

	/**
	 * Dobozos raktár priorizálása rendelésenként
	 */
	private function prioritizeStock(&$stock, $primaryWarehouse = null) {
		$primary = $primaryWarehouse ?? $this->primaryWarehouse;

		foreach ($stock as $itemId => &$bins) {
			usort($bins, function($a, $b) use ($primary) {
				$aCode = $this->warehouseConfig[$a['warehouse_id']]['code'] ?? '';
				$bCode = $this->warehouseConfig[$b['warehouse_id']]['code'] ?? '';

				$aIsMain = (strpos($aCode, $primary) === 0) ? 1 : 0;
				$bIsMain = (strpos($bCode, $primary) === 0) ? 1 : 0;

				if ($aIsMain !== $bIsMain) {
					return $bIsMain - $aIsMain;
				}

				return 0;
			});
		}
		unset($bins);
	}

	/**
	 * Fóliás raktár priorizálása rendelésenként
	 */
	private function prioritizeFoilStock(&$stock, $primaryWarehouse = null) {
		$primary = $primaryWarehouse ?? $this->primaryWarehouse;

		foreach ($stock as $itemId => &$bins) {
			usort($bins, function($a, $b) use ($primary) {
				$aIsMain = (strpos($a['pos'], $primary) === 0) ? 1 : 0;
				$bIsMain = (strpos($b['pos'], $primary) === 0) ? 1 : 0;

				if ($aIsMain !== $bIsMain) {
					return $bIsMain - $aIsMain;
				}

				return 0;
			});
		}
		unset($bins);
	}


	// ========================================================================
	// KISZEDÉSI LOGIKA
	// ========================================================================

	private function generatePickingList($orderData, $boxStock, $foilStock) {
		$pickingList = [];
		$localBoxStock = $boxStock;
		$localFoilStock = $foilStock;

		foreach ($orderData['items'] as $item) {
			$itemId = $item['item_id'];
			$need = $item['need'];
			$takenOut = $item['taken_out'];
			$ordered = $item['ordered'];
			$amountType = $item['amount_type'];
			$primarySource = $item['primary_source'];

			if ($takenOut > 0) {
				$pickingList[] = [
					'wid' => 0,
					'item_id' => $itemId,
					'pos' => '00',
					'code' => '',
					'amount' => $takenOut . ' / ' . $ordered,
					'amount_type' => $amountType,
					'location' => '',
					'type' => 'taken'
				];
			}

			if ($need <= 0) continue;

			$setInfo = $this->splitSetItemId($itemId);

			if ($setInfo === false) {
				$picks = $this->pickSimpleItem($itemId, $need, $amountType, $primarySource, $localBoxStock, $localFoilStock);
				$pickingList = array_merge($pickingList, $picks);
			} else {
				if ($this->isCLSet($itemId)) {
					$picks = $this->pickCLSet($itemId, $setInfo, $need, $amountType, $localBoxStock);
					$pickingList = array_merge($pickingList, $picks);
				} else {
					$picks = $this->pickNormalSet($itemId, $setInfo, $need, $amountType, $primarySource, $localBoxStock, $localFoilStock);
					$pickingList = array_merge($pickingList, $picks);
				}
			}

			if ($need > 0) {
				$pickingList[] = [
					'wid' => -1,
					'item_id' => $itemId,
					'pos' => '00',
					'code' => '',
					'amount' => $need,
					'amount_type' => $amountType,
					'location' => '',
					'type' => 'missing'
				];
			}
		}

		return $pickingList;
	}

	private function pickSimpleItem($itemId, &$need, $amountType, $primarySource, &$boxStock, &$foilStock) {
		$picks = [];
		$sources = ($primarySource === 1) ? ['foil', 'box'] : ['box', 'foil'];

		foreach ($sources as $sourceType) {
			if ($need <= 0) break;

			if ($amountType !== 0 && $sourceType !== 'foil') {
				continue;
			}

			if ($sourceType === 'box' && isset($boxStock[$itemId])) {
				$picks = array_merge($picks, $this->pickFromBoxStock($itemId, $need, $amountType, $boxStock));
			}

			if ($sourceType === 'foil' && isset($foilStock[$itemId])) {
				$picks = array_merge($picks, $this->pickFromFoilStock($itemId, $need, $amountType, $foilStock));
			}
		}

		return $picks;
	}

	private function pickFromBoxStock($itemId, &$need, $amountType, &$stock) {
		$picks = [];

		while ($need > 0 && isset($stock[$itemId]) && !empty($stock[$itemId])) {
			$bin = &$stock[$itemId][0];
			$take = min($bin['amount'], $need);

			$picks[] = [
				'wid' => $bin['wid'],
				'item_id' => $itemId,
				'pos' => $this->formatWarehousePosition($bin['pos'], $bin['warehouse_id']),
				'code' => $this->warehouseConfig[$bin['warehouse_id']]['code'] ?? '',
				'amount' => $take,
				'amount_type' => $amountType,
				'location' => ($this->warehouseConfig[$bin['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin['warehouse_id']]['name'] ?? ''),
				'type' => 'stock'
			];

			$bin['amount'] -= $take;
			$need -= $take;

			if ($bin['amount'] <= 0) {
				array_shift($stock[$itemId]);
			}
		}

		return $picks;
	}

	private function pickFromFoilStock($itemId, &$need, $amountType, &$stock) {
		$picks = [];

		while ($need > 0 && isset($stock[$itemId]) && !empty($stock[$itemId])) {
			$bin = &$stock[$itemId][0];

			if ($amountType === 0) {
				$take = min($bin['amount'], $need);
			} elseif ($amountType === 1) {
				$take = min($bin['right_db'], $need);
			} elseif ($amountType === 2) {
				$take = min($bin['left_db'], $need);
			} else {
				$take = 0;
			}

			if ($take <= 0) {
				array_shift($stock[$itemId]);
				continue;
			}

			$picks[] = [
				'wid' => $bin['wid'],
				'item_id' => $itemId,
				'pos' => $bin['pos'],
				'code' => '',
				'amount' => $take,
				'amount_type' => $amountType,
				'location' => self::FOIL_WAREHOUSE_LABEL,
				'type' => 'foil'
			];

			if ($amountType === 0) {
				$bin['right_db'] -= $take;
				$bin['left_db'] -= $take;
			} elseif ($amountType === 1) {
				$bin['right_db'] -= $take;
			} elseif ($amountType === 2) {
				$bin['left_db'] -= $take;
			}

			$bin['amount'] = min($bin['right_db'], $bin['left_db']);
			$need -= $take;

			if ($bin['right_db'] <= 0 && $bin['left_db'] <= 0) {
				array_shift($stock[$itemId]);
			}
		}

		return $picks;
	}

	private function pickCLSet($itemId, $setInfo, &$need, $amountType, &$boxStock) {
		$picks = [];
		$item1 = $setInfo['item1'];
		$item2 = $setInfo['item2'];

		$available1 = 0;
		$available2 = 0;

		if (isset($boxStock[$item1])) {
			foreach ($boxStock[$item1] as $bin) {
				$available1 += $bin['amount'];
			}
		}

		if (isset($boxStock[$item2])) {
			foreach ($boxStock[$item2] as $bin) {
				$available2 += $bin['amount'];
			}
		}

		$maxCanTake = min($available1, $available2, $need);

		if ($maxCanTake > 0) {
			$tempNeed = $maxCanTake;

			while ($tempNeed > 0 && !empty($boxStock[$item1]) && !empty($boxStock[$item2])) {
				$bin1 = &$boxStock[$item1][0];
				$bin2 = &$boxStock[$item2][0];

				$take = min($bin1['amount'], $bin2['amount'], $tempNeed);

				$pos1 = $this->formatWarehousePosition($bin1['pos'], $bin1['warehouse_id']);
				$pos2 = $this->formatWarehousePosition($bin2['pos'], $bin2['warehouse_id']);

        $picks[] = [
        	'wid' => $bin1['wid'] . '/' . $bin2['wid'],
        	'item_id' => $itemId,
        	'pos' => $pos1 . '<br>' . $pos2,  // + helyett <br>
        	'code' => '',
        	'amount' => $take,
        	'amount_type' => $amountType,
        	'location' => ($this->warehouseConfig[$bin1['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin1['warehouse_id']]['name'] ?? '') . '<br>' . ($this->warehouseConfig[$bin2['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin2['warehouse_id']]['name'] ?? ''),
        	'type' => 'stock'
        ];

				$bin1['amount'] -= $take;
				$bin2['amount'] -= $take;
				$tempNeed -= $take;

				if ($bin1['amount'] <= 0) {
					array_shift($boxStock[$item1]);
				}
				if ($bin2['amount'] <= 0) {
					array_shift($boxStock[$item2]);
				}
			}

			$need -= $maxCanTake;
		}

		return $picks;
	}

	private function pickNormalSet($itemId, $setInfo, &$need, $amountType, $primarySource, &$boxStock, &$foilStock) {
		$picks = [];
		$item1 = $setInfo['item1'];
		$item2 = $setInfo['item2'];

		$sources = ($primarySource === 1) ? ['foil', 'box', 'hybrid'] : ['box', 'foil', 'hybrid'];

		foreach ($sources as $sourceType) {
			if ($need <= 0) break;

			if ($sourceType === 'box' && isset($boxStock[$itemId])) {
				while ($need > 0 && !empty($boxStock[$itemId])) {
					$bin = &$boxStock[$itemId][0];
					$take = min($bin['amount'], $need);

					$picks[] = [
						'wid' => $bin['wid'],
						'item_id' => $itemId,
						'pos' => $this->formatWarehousePosition($bin['pos'], $bin['warehouse_id']),
						'code' => $this->warehouseConfig[$bin['warehouse_id']]['code'] ?? '',
						'amount' => $take,
						'amount_type' => $amountType,
						'location' => ($this->warehouseConfig[$bin['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin['warehouse_id']]['name'] ?? ''),
						'type' => 'stock'
					];

					$bin['amount'] -= $take;
					$need -= $take;

					if ($bin['amount'] <= 0) {
						array_shift($boxStock[$itemId]);
					}
				}
			}

			if ($sourceType === 'foil' && $need > 0) {
				$picks = array_merge($picks, $this->pickSetFromFoil($itemId, $item1, $item2, $need, $amountType, $foilStock));
			}

			if ($sourceType === 'hybrid' && $need > 0) {
				$picks = array_merge($picks, $this->pickSetHybrid($itemId, $item1, $item2, $need, $amountType, $boxStock, $foilStock));
			}
		}

		return $picks;
	}

	private function pickSetFromFoil($itemId, $item1, $item2, &$need, $amountType, &$foilStock) {
		$picks = [];

		$available1 = 0;
		$available2 = 0;

		if (isset($foilStock[$item1])) {
			foreach ($foilStock[$item1] as $bin) {
				$available1 += $bin['amount'];
			}
		}

		if (isset($foilStock[$item2])) {
			foreach ($foilStock[$item2] as $bin) {
				$available2 += $bin['amount'];
			}
		}

		$maxCanTake = min($available1, $available2, $need);

		if ($maxCanTake > 0) {
			$tempNeed = $maxCanTake;

			while ($tempNeed > 0 && !empty($foilStock[$item1]) && !empty($foilStock[$item2])) {
				$bin1 = &$foilStock[$item1][0];
				$bin2 = &$foilStock[$item2][0];

				$take = min($bin1['amount'], $bin2['amount'], $tempNeed);

				if ($take > 0) {
          $picks[] = [
          	'wid' => $bin1['wid'] . '/' . $bin2['wid'],
          	'item_id' => $itemId,
          	'pos' => $bin1['pos'] . '<br>' . $bin2['pos'],  // + helyett <br>
          	'code' => '',
          	'amount' => $take,
          	'amount_type' => $amountType,
          	'location' => self::FOIL_WAREHOUSE_LABEL,
          	'type' => 'foil'
          ];

					$bin1['amount'] -= $take;
					$bin2['amount'] -= $take;
					$bin1['right_db'] -= $take;
					$bin1['left_db'] -= $take;
					$bin2['right_db'] -= $take;
					$bin2['left_db'] -= $take;
					$tempNeed -= $take;
				}

				if ($bin1['amount'] <= 0) {
					array_shift($foilStock[$item1]);
				}
				if ($bin2['amount'] <= 0) {
					array_shift($foilStock[$item2]);
				}
			}

			$need -= $maxCanTake;
		}

		return $picks;
	}

	private function pickSetHybrid($itemId, $item1, $item2, &$need, $amountType, &$boxStock, &$foilStock) {
		$picks = [];

		// item1 dobozból, item2 fóliásból
		$availableBox1 = 0;
		$availableFoil2 = 0;

		if (isset($boxStock[$item1])) {
			foreach ($boxStock[$item1] as $bin) {
				$availableBox1 += $bin['amount'];
			}
		}

		if (isset($foilStock[$item2])) {
			foreach ($foilStock[$item2] as $bin) {
				$availableFoil2 += $bin['amount'];
			}
		}

		$maxCanTake1 = min($availableBox1, $availableFoil2, $need);

		if ($maxCanTake1 > 0) {
			$tempNeed = $maxCanTake1;

			while ($tempNeed > 0 && !empty($boxStock[$item1]) && !empty($foilStock[$item2])) {
				$bin1 = &$boxStock[$item1][0];
				$bin2 = &$foilStock[$item2][0];

				$take = min($bin1['amount'], $bin2['amount'], $tempNeed);

				if ($take > 0) {
					$pos1 = $this->formatWarehousePosition($bin1['pos'], $bin1['warehouse_id']);

          $picks[] = [
          	'wid' => $bin1['wid'] . '/' . $bin2['wid'],
          	'item_id' => $itemId,
          	'pos' => $pos1 . '<br>' . $bin2['pos'],  // + helyett <br>
          	'code' => '',
          	'amount' => $take,
          	'amount_type' => $amountType,
          	'location' => ($this->warehouseConfig[$bin1['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin1['warehouse_id']]['name'] ?? '') . '<br>' . self::FOIL_WAREHOUSE_LABEL,
          	'type' => 'hybrid'
          ];

					$bin1['amount'] -= $take;
					$bin2['amount'] -= $take;
					$bin2['right_db'] -= $take;
					$bin2['left_db'] -= $take;
					$tempNeed -= $take;
				}

				if ($bin1['amount'] <= 0) {
					array_shift($boxStock[$item1]);
				}
				if ($bin2['amount'] <= 0) {
					array_shift($foilStock[$item2]);
				}
			}

			$need -= $maxCanTake1;
		}

		// item1 fóliásból, item2 dobozból
		if ($need > 0) {
			$availableFoil1 = 0;
			$availableBox2 = 0;

			if (isset($foilStock[$item1])) {
				foreach ($foilStock[$item1] as $bin) {
					$availableFoil1 += $bin['amount'];
				}
			}

			if (isset($boxStock[$item2])) {
				foreach ($boxStock[$item2] as $bin) {
					$availableBox2 += $bin['amount'];
				}
			}

			$maxCanTake2 = min($availableFoil1, $availableBox2, $need);

			if ($maxCanTake2 > 0) {
				$tempNeed = $maxCanTake2;

				while ($tempNeed > 0 && !empty($foilStock[$item1]) && !empty($boxStock[$item2])) {
					$bin1 = &$foilStock[$item1][0];
					$bin2 = &$boxStock[$item2][0];

					$take = min($bin1['amount'], $bin2['amount'], $tempNeed);

					if ($take > 0) {
						$pos2 = $this->formatWarehousePosition($bin2['pos'], $bin2['warehouse_id']);

            $picks[] = [
            	'wid' => $bin1['wid'] . '/' . $bin2['wid'],
            	'item_id' => $itemId,
            	'pos' => $bin1['pos'] . '<br>' . $pos2,  // + helyett <br>
            	'code' => '',
            	'amount' => $take,
            	'amount_type' => $amountType,
            	'location' => self::FOIL_WAREHOUSE_LABEL . '<br>' . ($this->warehouseConfig[$bin2['warehouse_id']]['location'] ?? '') . ' / ' . ($this->warehouseConfig[$bin2['warehouse_id']]['name'] ?? ''),
            	'type' => 'hybrid'
            ];

						$bin1['amount'] -= $take;
						$bin1['right_db'] -= $take;
						$bin1['left_db'] -= $take;
						$bin2['amount'] -= $take;
						$tempNeed -= $take;
					}

					if ($bin1['amount'] <= 0) {
						array_shift($foilStock[$item1]);
					}
					if ($bin2['amount'] <= 0) {
						array_shift($boxStock[$item2]);
					}
				}

				$need -= $maxCanTake2;
			}
		}

		return $picks;
	}

	// ========================================================================
	// SEGÉD METÓDUSOK
	// ========================================================================

  /**
   * Szett item_id szétbontása
   * Ha van prefix (betű), akkor vezető nullák maradnak
   * Ha nincs prefix, akkor vezető nullák eltávolítása
   */
  private function splitSetItemId($itemId) {
  	// Ellenőrzés: tartalmaz-e '+' jelet
  	if (strpos($itemId, '+') === false) {
  		return false;
  	}

  	$parts = explode('+', $itemId, 2);
  	$first = trim($parts[0]);
  	$second = trim($parts[1]);

  	// Prefix detektálás (nem számjegy karakterek az elején)
  	$prefix = '';
  	$firstNumeric = $first;

  	if (preg_match('/^([^0-9]{1,2})(.*)$/', $first, $matches)) {
  		// Van prefix (pl. CL, FR, LR)
  		$prefix = $matches[1];
  		$firstNumeric = $matches[2];

  		// Prefix esetén vezető nullák MARADNAK
  		$item1 = $prefix . $firstNumeric;
  		$item2 = $prefix . $second;
  	} else {
  		// Nincs prefix - csak számok
  		// Vezető nullák ELTÁVOLÍTÁSA
  		$item1 = (string)intval($first);
  		$item2 = (string)intval($second);
  	}

  	return ['item1' => $item1, 'item2' => $item2];
  }


	private function isCLSet($itemId) {
		return substr($itemId, 0, 2) === self::CL_SET_PREFIX;
	}

	private function formatWarehousePosition($position, $warehouseId) {
		$parts = explode(':', $position);
		if (count($parts) === 2) {
			$letter = $this->numberToLetter(intval($parts[0]));
			$number = $this->padZero($parts[1]);
			$code = $this->warehouseConfig[$warehouseId]['code'] ?? '';
			return $code . $letter . $number;
		}
		return $position;
	}

	private function numberToLetter($n) {
		if ($n < 1 || $n > 26) return null;
		return chr(64 + $n);
	}

	private function padZero($str) {
		return str_pad(strval($str), 2, '0', STR_PAD_LEFT);
	}

	private function formatPositionForSort(&$pickingList) {
		foreach ($pickingList as &$entry) {
			if (strpos($entry['pos'], ',') !== false) {
				list($row, $col) = explode(',', $entry['pos']);
				$entry['pos'] = $col . '~' . $row;
			}
		}
		unset($entry);
	}

  private function sortPickingList(&$pickingList) {
  	$typeOrder = [
  		'foil' => 1,
  		'stock' => 2,
  		'hybrid' => 2,
  		'taken' => 3,
  		'missing' => 4
  	];

  	usort($pickingList, function($a, $b) use ($typeOrder) {
  		$aType = $a['type'] ?? 'stock';
  		$bType = $b['type'] ?? 'stock';

  		$aPriority = $typeOrder[$aType] ?? 3;
  		$bPriority = $typeOrder[$bType] ?? 3;

  		if ($aPriority != $bPriority) {
  			return $aPriority - $bPriority;
  		}

  		if ($aType === 'taken' || $aType === 'missing') {
  			return strcmp($a['item_id'], $b['item_id']);
  		}

  		// Lokáció természetes rendezés
  		$locCmp = strnatcmp($a['location'], $b['location']);
  		if ($locCmp != 0) {
  			return $locCmp;
  		}

  		// Pozíció természetes rendezés
  		if (in_array($aType, ['stock', 'hybrid'])) {
  			if (strpos($a['pos'], '~') !== false && strpos($b['pos'], '~') !== false) {
  				list($aCol, $aRow) = explode('~', $a['pos']);
  				list($bCol, $bRow) = explode('~', $b['pos']);

  				if (intval($aRow) != intval($bRow)) {
  					return intval($aRow) - intval($bRow);
  				}
  				if (intval($aCol) != intval($bCol)) {
  					return intval($aCol) - intval($bCol);
  				}
  			}
  		}

  		// Természetes rendezés pozíción (FŐ01, FŐ02, ... FŐ10)
  		$posCmp = strnatcmp($a['pos'], $b['pos']);
  		if ($posCmp != 0) {
  			return $posCmp;
  		}

  		return strcmp($a['item_id'], $b['item_id']);
  	});
  }


  private function sortOrders(&$orders) {
  	$typeOrder = [
  		'foil' => 1,
  		'stock' => 2,
  		'hybrid' => 2,
  		'taken' => 3,
  		'missing' => 4
  	];

  	usort($orders, function($a, $b) use ($typeOrder) {
  		if (empty($a['picking_list']) || empty($b['picking_list'])) {
  			return 0;
  		}

  		$aFirst = $a['picking_list'][0];
  		$bFirst = $b['picking_list'][0];

  		$aType = $aFirst['type'] ?? 'stock';
  		$bType = $bFirst['type'] ?? 'stock';

  		$aPriority = $typeOrder[$aType] ?? 3;
  		$bPriority = $typeOrder[$bType] ?? 3;

  		if ($aPriority != $bPriority) {
  			return $aPriority - $bPriority;
  		}

  		// Természetes rendezés lokáción
  		$locCmp = strnatcmp($aFirst['location'], $bFirst['location']);
  		if ($locCmp != 0) {
  			return $locCmp;
  		}

  		// Pozíció rendezés
  		if (in_array($aType, ['stock', 'hybrid'])) {
  			if (strpos($aFirst['pos'], '~') !== false && strpos($bFirst['pos'], '~') !== false) {
  				list($aCol, $aRow) = explode('~', $aFirst['pos']);
  				list($bCol, $bRow) = explode('~', $bFirst['pos']);

  				if (intval($aRow) != intval($bRow)) {
  					return intval($aRow) - intval($bRow);
  				}
  				if (intval($aCol) != intval($bCol)) {
  					return intval($aCol) - intval($bCol);
  				}
  			}
  		}

  		// Természetes rendezés pozíción
  		return strnatcmp($aFirst['pos'], $bFirst['pos']);
  	});
  }


	private function determineIcon($pickingList) {
		foreach ($pickingList as $entry) {
			if ($entry['wid'] != 0) {
				return '✓';
			}
		}
		return '';
	}
}

/**
 * Raktárból Eltávolítás Osztály
 *
 * Funkció: Kiszedett termékek eltávolítása a raktárból és lefoglalás frissítése
 *
 */

class WarehouseRemover {

	private $conn;

	/**
	 * Konstruktor
	 */
	public function __construct($conn) {
		$this->conn = $conn;
	}

	/**
	 * Statikus hívás
	 */
	public static function remove($conn, $inputData, $coderclass) {
		$remover = new self($conn);
		return $remover->processRemoval($inputData, $coderclass);
	}

	/**
	 * Eltávolítás feldolgozás
	 */
	private function processRemoval($inputData, $coderclass) {
		// Input adatok kinyerése és tisztítása
		$wid = htmlspecialchars($inputData['wid'] ?? '');
		$amount = htmlspecialchars($inputData['amount'] ?? '');
		$item_id = htmlspecialchars(str_replace(' ', '+', $inputData['item_id'] ?? ''));
		$corrigate = htmlspecialchars($inputData['corrigate'] ?? 'false');
		$overal_order_id = $inputData['overal_order_id'] ?? '';
		$encoded_reason = htmlspecialchars($inputData['reason'] ?? '');

		// Order ID és order name feldolgozása
		$order_info = $this->parseOrderInfo($overal_order_id);
		$order_name = $order_info['order_name'];
		$order_id = $order_info['order_id'];

		// Reason dekódolás
		$decoded_data = $coderclass->decode($encoded_reason, 'SZ4TUN4');
		$data_parts = explode('|', $decoded_data);

		if (count($data_parts) != 2) {
			return ['result' => 'error', 'message' => 'Érvénytelen kiszedési lista azonosító'];
		}

		$reason = $data_parts[0];
		$order_date = $data_parts[1];

		// Amount type feldolgozása
		$amount_info = $this->parseAmount($amount);
		$amount_type = $amount_info['amount_type'];
		$amount_value = $amount_info['amount'];

		// WID lista feldolgozása és szétválogatása
		$wid_info = $this->parseWids($wid);
		$foil_wids = $wid_info['foil_wids'];
		$box_wids = $wid_info['box_wids'];

		// Raktár ellenőrzés
		$warehouse_check = $this->checkWarehouseStock($foil_wids, $box_wids, $amount_value, $amount_type);
		if (!$warehouse_check['success']) {
			return $warehouse_check;
		}

		$warehouse_foil_list = $warehouse_check['foil_list'];
		$warehouse_box_list = $warehouse_check['box_list'];

		// Lefoglalás ellenőrzés
		$reservation_check = $this->checkReservation($item_id, $order_date, $reason, $order_name, $order_id, $amount_type, $amount_value);
		if (!$reservation_check['success']) {
			return $reservation_check;
		}

		$reserv_id = $reservation_check['reserv_id'];
		$new_taken_out = $reservation_check['new_taken_out'];

		// Lefoglalás frissítése
		$this->updateReservation($reserv_id, $new_taken_out);

		// Raktár frissítése
		$update_result = $this->updateWarehouseStock(
			$foil_wids,
			$box_wids,
			$warehouse_foil_list,
			$warehouse_box_list,
			$amount_value,
			$amount_type,
			$corrigate
		);

		return [
			'result' => 'ok',
			'reset' => $update_result['reset'],
			'message' => $update_result['message']
		];
	}

	/**
	 * Order info feldolgozás
	 */
	private function parseOrderInfo($overal_order_id) {
		$order_name = '';
		$order_id = '';

		if ($overal_order_id != '!&maradek&! ') {
			$last_dash_pos = strrpos($overal_order_id, ' - ');

			if ($last_dash_pos !== false) {
				$order_name = htmlspecialchars(substr($overal_order_id, 0, $last_dash_pos));
				$order_part = substr($overal_order_id, $last_dash_pos + 3);
				$order_id = substr($order_part, 1, -1);
			} else {
				$order_name = htmlspecialchars($overal_order_id);
			}
		}

		return ['order_name' => $order_name, 'order_id' => $order_id];
	}

	/**
	 * Amount feldolgozás (J/B prefix kezelése)
	 */
	private function parseAmount($amount) {
		$amount_type = 0;

		if (substr($amount, 0, 1) == "J") {
			$amount_type = 1;
			$amount = substr($amount, 1);
		} else if (substr($amount, 0, 1) == "B") {
			$amount_type = 2;
			$amount = substr($amount, 1);
		}

		return ['amount_type' => $amount_type, 'amount' => intval($amount)];
	}

	/**
	 * WID lista feldolgozása és szétválogatása
	 */
	private function parseWids($wid) {
		$wid_list = explode('/', $wid);
		$foil_wids = [];
		$box_wids = [];

		foreach ($wid_list as $single_wid) {
			if (substr($single_wid, 0, 1) == "F") {
				$foil_wids[] = substr($single_wid, 1);
			} else {
				$box_wids[] = $single_wid;
			}
		}

		return ['foil_wids' => $foil_wids, 'box_wids' => $box_wids];
	}

	/**
	 * Raktár készlet ellenőrzés
	 */
	private function checkWarehouseStock($foil_wids, $box_wids, $amount, $amount_type) {
		$warehouse_foil_list = [];
		$warehouse_box_list = [];

		// Fóliás raktár ellenőrzés
		foreach ($foil_wids as $foil_wid) {
			$stmt = $this->conn->prepare("SELECT * FROM warehouse_foil WHERE wid = ? AND is_active = 1");
			$stmt->bind_param("i", $foil_wid);
			$stmt->execute();
			$result = $stmt->get_result();
			$warehouse_foil = $result->fetch_assoc();
			$stmt->close();

			if ($warehouse_foil == null) {
				return [
					'success' => false,
					'result' => 'error',
					'reset' => 'true',
					'message' => 'Érvénytelen fóliás raktár azonosító (WID: F' . $foil_wid . ')'
				];
			}

			// Mennyiség ellenőrzés
			if ($amount_type == 1) {
				$inWarehouse = intval($warehouse_foil['right_db']);
			} else if ($amount_type == 2) {
				$inWarehouse = intval($warehouse_foil['left_db']);
			} else {
				$inWarehouse = min(intval($warehouse_foil['right_db']), intval($warehouse_foil['left_db']));
			}

			if ($inWarehouse < $amount) {
				return [
					'success' => false,
					'result' => 'error',
					'reset' => 'true',
					'message' => 'Érvénytelen raktár bejegyzés, a fóliás raktár szerint nincs ennyi termék a polcon (van: ' . $inWarehouse . ', kell: ' . $amount . ')'
				];
			}

			$warehouse_foil_list[] = $warehouse_foil;
		}

		// Dobozos raktár ellenőrzés
		foreach ($box_wids as $box_wid) {
			$stmt = $this->conn->prepare("SELECT * FROM warehouse WHERE wid = ?");
			$stmt->bind_param("i", $box_wid);
			$stmt->execute();
			$result = $stmt->get_result();
			$warehouse_box = $result->fetch_assoc();
			$stmt->close();

			if ($warehouse_box == null) {
				return [
					'success' => false,
					'result' => 'error',
					'reset' => 'true',
					'message' => 'Érvénytelen dobozos raktár azonosító (WID: ' . $box_wid . ')'
				];
			}

			if (intval($warehouse_box['amount']) < $amount) {
				return [
					'success' => false,
					'result' => 'error',
					'reset' => 'true',
					'message' => 'Érvénytelen raktár bejegyzés, a dobozos raktár szerint nincs ennyi termék a polcon (van: ' . $warehouse_box['amount'] . ', kell: ' . $amount . ')'
				];
			}

			$warehouse_box_list[] = $warehouse_box;
		}

		return [
			'success' => true,
			'foil_list' => $warehouse_foil_list,
			'box_list' => $warehouse_box_list
		];
	}

	/**
	 * Lefoglalás ellenőrzés
	 */
	private function checkReservation($item_id, $order_date, $reason, $order_name, $order_id, $amount_type, $amount) {
		$stmt = $this->conn->prepare("SELECT * FROM warehouse_reservation
			WHERE DATE(FROM_UNIXTIME(date_create)) = ?
			AND item_id = ?
			AND reason = ?
			AND order_name = ?
			AND order_id = ?
			AND is_active = 1
			AND amount_type = ?");
		$stmt->bind_param("sssssi", $order_date, $item_id, $reason, $order_name, $order_id, $amount_type);
		$stmt->execute();
		$result = $stmt->get_result();
		$reservation_sql = $result->fetch_assoc();
		$stmt->close();

		if ($reservation_sql == null) {
			return [
				'success' => false,
				'result' => 'error',
				'reset' => 'true',
				'message' => 'Érvénytelen rendelési azonosító (item: ' . $item_id . ', order: ' . $order_name . ', date: ' . $order_date . ')'
			];
		}

		$reserv_id = $reservation_sql['reserv_id'];
		$current_taken_out = intval($reservation_sql['taken_out']);
		$ordered_amount = intval($reservation_sql['amount']);
		$new_taken_out = $current_taken_out + $amount;

		if ($new_taken_out > $ordered_amount) {
			return [
				'success' => false,
				'result' => 'error',
				'reset' => 'true',
				'message' => 'Hiba történt a mennyiségek megadásakor! Több terméket akart kiszedni mint amennyi a rendelésben szerepel. (Rendelt: ' . $ordered_amount . ', már kiszedve: ' . $current_taken_out . ', most kiszedni kívánt: ' . $amount . ')'
			];
		}

		if ($new_taken_out < 0) {
			return [
				'success' => false,
				'result' => 'error',
				'reset' => 'true',
				'message' => 'Hiba történt a mennyiségek megadásakor, negatív érték nem megengedett!'
			];
		}

		return [
			'success' => true,
			'reserv_id' => $reserv_id,
			'new_taken_out' => $new_taken_out
		];
	}

	/**
	 * Lefoglalás frissítése
	 */
	private function updateReservation($reserv_id, $new_taken_out) {
		$stmt = $this->conn->prepare("UPDATE warehouse_reservation SET taken_out = ? WHERE reserv_id = ?");
		$stmt->bind_param("ii", $new_taken_out, $reserv_id);
		$stmt->execute();
		$stmt->close();
	}

	/**
	 * Raktár készlet frissítése
	 */
	private function updateWarehouseStock($foil_wids, $box_wids, $warehouse_foil_list, $warehouse_box_list, $amount, $amount_type, $corrigate) {
		$reset = 'false';
		$message = '';

		// Fóliás raktár frissítése
		foreach ($foil_wids as $idx => $foil_wid) {
			$warehouse_foil = $warehouse_foil_list[$idx];

			// Mennyiség számítás
			if ($amount_type == 1) {
				$left_db = 0;
				$right_db = -$amount;
			} else if ($amount_type == 2) {
				$left_db = -$amount;
				$right_db = 0;
			} else {
				$left_db = -$amount;
				$right_db = -$amount;
			}

			// Korrigálás
			if ($corrigate == 'true') {
				if ($amount_type == 1) {
					$left_db = 0;
					$right_db = -intval($warehouse_foil['right_db']);
				} else if ($amount_type == 2) {
					$left_db = -intval($warehouse_foil['left_db']);
					$right_db = 0;
				} else {
					if (intval($warehouse_foil['right_db']) > intval($warehouse_foil['left_db'])) {
						$left_db = -intval($warehouse_foil['left_db']);
						$right_db = -intval($warehouse_foil['left_db']);
					} else {
						$left_db = -intval($warehouse_foil['right_db']);
						$right_db = -intval($warehouse_foil['right_db']);
					}
				}

				$reset = 'true';
				$message = 'A raktár megfelelő polcán a mennyiség nullázva lett';
			}

			// Frissítés
			$stmt = $this->conn->prepare("UPDATE warehouse_foil
				SET left_db = CASE WHEN left_db + ? < 0 THEN 0 ELSE left_db + ? END,
				    right_db = CASE WHEN right_db + ? < 0 THEN 0 ELSE right_db + ? END
				WHERE wid = ?");
			$stmt->bind_param("iiiii", $left_db, $left_db, $right_db, $right_db, $foil_wid);
			$stmt->execute();
			$stmt->close();
		}

		// Dobozos raktár frissítése
		foreach ($box_wids as $idx => $box_wid) {
			$warehouse_box = $warehouse_box_list[$idx];

			$new_amount_in_warehouse = 0;

			if ($corrigate == 'true') {
				$new_amount_in_warehouse = 0;
				$reset = 'true';
				$message = 'A raktár megfelelő polcán a mennyiség nullázva lett';
			} else {
				$new_amount_in_warehouse = intval($warehouse_box['amount']) - $amount;
			}

			$stmt = $this->conn->prepare("UPDATE warehouse SET amount = ? WHERE wid = ?");
			$stmt->bind_param("ii", $new_amount_in_warehouse, $box_wid);
			$stmt->execute();
			$stmt->close();
		}

		return ['reset' => $reset, 'message' => $message];
	}
}
?>
