<?php

namespace App\Services\PwApi\Item;

class ItemService extends \App\Services\PwApi\Core\PwApiService
{
    public function getRoleStatus($role): ?array
    {
        $pack = pack('N*', -1, $role);
        $pack = $this->gamed->createHeader($this->data['code']['getRoleStatus'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);

        $result = $this->gamed->unmarshal($data, $this->data['role']['status']);

        return is_array($result) ? $result : null;
    }

    public function getRoleInventory($role)
    {
        $pack = pack('N*', -1, $role);
        $pack = $this->gamed->createHeader($this->data['code']['getRoleInventory'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);

        return $this->gamed->unmarshal($data, $this->data['role']['pocket']);
    }

    public function getRoleEquipment($role)
    {
        $pack = pack('N*', -1, $role);
        $pack = $this->gamed->createHeader($this->data['code']['getRoleEquipment'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);

        return $this->gamed->unmarshal($data, $this->data['role']['equipment']);
    }

    public function getRoleStorehouse($role)
    {
        $pack = pack('N*', -1, $role);
        $pack = $this->gamed->createHeader($this->data['code']['getRoleStoreHouse'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);

        return $this->gamed->unmarshal($data, $this->data['role']['storehouse']);
    }

    public function getRoleTask($role)
    {
        $pack = pack('N*', -1, $role);
        $pack = $this->gamed->createHeader($this->data['code']['getRoleTask'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);

        return $this->gamed->unmarshal($data, $this->data['role']['task']);
    }

    public function getRoleBase($role): ?array
    {
        $pack = pack('N*', -1, $role);
        $pack = $this->gamed->createHeader($this->data['code']['getRoleBase'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);

        $result = $this->gamed->unmarshal($data, $this->data['role']['base']);

        return is_array($result) ? $result : null;
    }

    public function putRole($role, $params)
    {
        if (isset($params['equipment']['eqp']['id'])) {
            $tmp = $params['equipment']['eqp'];
            $params['equipment']['eqp'] = [];
            $params['equipment']['eqp'][] = $tmp;
        }
        if (isset($params['pocket']['inv']['id'])) {
            $tmp = $params['pocket']['inv'];
            $params['pocket']['inv'] = [];
            $params['pocket']['inv'][] = $tmp;
        }
        if (isset($params['storehouse']['store']['id'])) {
            $tmp = $params['storehouse']['store'];
            $params['storehouse']['store'] = [];
            $params['storehouse']['store'][] = $tmp;
        }
        if (isset($params['task']['task_inventory']['id'])) {
            $tmp = $params['task']['task_inventory'];
            $params['task']['task_inventory'] = [];
            $params['task']['task_inventory'][] = $tmp;
        }
        if (isset($params['storehouse']['dress']['id'])) {
            $tmp = $params['storehouse']['dress'];
            $params['storehouse']['dress'] = [];
            $params['storehouse']['dress'][] = $tmp;
        }
        if (isset($params['storehouse']['material']['id'])) {
            $tmp = $params['storehouse']['material'];
            $params['storehouse']['material'] = [];
            $params['storehouse']['material'][] = $tmp;
        }
        if (config('pw-api.game_version') != '07') {
            $pack = pack('NNC*', -1, $role, 1).$this->gamed->marshal($params, $this->data['role']);

            return $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $pack));
        } else {
            $pack = pack('NNC*', -1, $role).$this->gamed->marshal($params['base'], $this->data['role']['base']);
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleBase'], $pack));
            $pack = pack('NNC*', -1, $role).$this->gamed->marshal($params['status'], $this->data['role']['status']);
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleStatus'], $pack));
            $pack = pack('NNC*', -1, $role).$this->gamed->marshal($params['pocket'], $this->data['role']['pocket']);
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleInventory'], $pack));
            $pack = pack('NNC*', -1, $role).$this->gamed->marshal($params['equipment'], $this->data['role']['equipment']);
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleEquipment'], $pack));
            $pack = pack('NNC*', -1, $role).$this->gamed->marshal($params['storehouse'], $this->data['role']['storehouse']);
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleStoreHouse'], $pack));
            $pack = pack('NNC*', -1, $role).$this->gamed->marshal($params['task'], $this->data['role']['task']);

            return $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleTask'], $pack));
        }
    }

    public function updateItemCount($roleId, $itemId, $newCount, $itemPos = null)
    {
        $pocket = $this->getRoleInventory($roleId);
        if (! $pocket || ! isset($pocket['inv'])) {
            return ['success' => false, 'message' => 'Could not get inventory'];
        }

        $targetItem = null;
        foreach ($pocket['inv'] as $item) {
            if (($item['id'] ?? 0) == $itemId) {
                if ($itemPos !== null && ($item['pos'] ?? -1) != $itemPos) {
                    continue;
                }
                $targetItem = $item;
                break;
            }
        }

        if (! $targetItem) {
            return ['success' => false, 'message' => "Item ID $itemId not found in inventory"];
        }

        $oldCount = $targetItem['count'] ?? 0;
        $targetPos = $targetItem['pos'] ?? 0;

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 1000) {
            return ['success' => false, 'message' => 'Failed to get raw role data'];
        }

        $itemIdPacked = pack('N', $itemId);
        $posPacked = pack('N', $targetPos);
        $oldCountPacked = pack('N', $oldCount);

        $searchPattern = $itemIdPacked.$posPacked.$oldCountPacked;
        $offset = strpos($roleData, $searchPattern);

        if ($offset === false) {
            $searchPattern2 = $itemIdPacked.$posPacked;
            $offset = strpos($roleData, $searchPattern2);
            if ($offset === false) {
                return ['success' => false, 'message' => 'Could not find item in raw data'];
            }
        }

        $countOffset = $offset + 8;

        $newCountPacked = pack('N', $newCount);
        $newData = substr($roleData, 0, $countOffset);
        $newData .= $newCountPacked;
        $newData .= substr($roleData, $countOffset + 4);

        $putPack = pack('NNC', -1, $roleId, 1).$newData;
        $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

        sleep(1);
        $verifyPocket = $this->getRoleInventory($roleId);
        foreach ($verifyPocket['inv'] as $item) {
            if (($item['id'] ?? 0) == $itemId) {
                if (($item['count'] ?? 0) == $newCount) {
                    return [
                        'success' => true,
                        'message' => 'Item count updated successfully',
                        'item_id' => $itemId,
                        'old_count' => $oldCount,
                        'new_count' => $newCount,
                    ];
                }
                break;
            }
        }

        return ['success' => false, 'message' => 'Update failed - character might be online'];
    }

    public function updateItemData($roleId, $itemId, $itemPos, $newData)
    {
        $pocket = $this->getRoleInventory($roleId);
        if (! $pocket || ! isset($pocket['inv'])) {
            return ['success' => false, 'message' => 'Could not get inventory'];
        }

        $targetItem = null;
        foreach ($pocket['inv'] as $item) {
            if (($item['id'] ?? 0) == $itemId && ($item['pos'] ?? -1) == $itemPos) {
                $targetItem = $item;
                break;
            }
        }

        if (! $targetItem) {
            return ['success' => false, 'message' => "Item ID $itemId at pos $itemPos not found in inventory"];
        }

        $oldData = $targetItem['data'] ?? '';

        if ($oldData === $newData) {
            return ['success' => true, 'message' => 'Item data unchanged'];
        }

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 1000) {
            return ['success' => false, 'message' => 'Failed to get raw role data'];
        }

        $oldDataBin = ! empty($oldData) ? pack('H*', $oldData) : '';
        $newDataBin = ! empty($newData) ? pack('H*', $newData) : '';

        $itemIdPacked = pack('N', $itemId);
        $posPacked = pack('N', $itemPos);
        $countPacked = pack('N', $targetItem['count'] ?? 0);
        $maxCountPacked = pack('N', $targetItem['max_count'] ?? 1);

        $searchPattern = $itemIdPacked.$posPacked.$countPacked.$maxCountPacked;
        $itemOffset = strpos($roleData, $searchPattern);

        if ($itemOffset === false) {
            $searchPattern = $itemIdPacked.$posPacked.$countPacked;
            $itemOffset = strpos($roleData, $searchPattern);
        }

        if ($itemOffset === false) {
            return ['success' => false, 'message' => 'Could not find item in raw data'];
        }

        $dataOffset = $itemOffset + 16;

        $oldDataLen = strlen($oldDataBin);
        $oldOctets = $this->gamed->cuint($oldDataLen).$oldDataBin;

        $newDataLen = strlen($newDataBin);
        $newOctets = $this->gamed->cuint($newDataLen).$newDataBin;

        $searchStart = max(0, $dataOffset - 10);
        $searchEnd = min(strlen($roleData), $dataOffset + 100);
        $searchArea = substr($roleData, $searchStart, $searchEnd - $searchStart);

        $octetsPos = strpos($searchArea, $oldOctets);
        if ($octetsPos === false) {
            if ($oldDataLen == 0) {
                $emptyOctet = $this->gamed->cuint(0);
                $octetsPos = strpos($searchArea, $emptyOctet);
            }
        }

        if ($octetsPos === false) {
            return ['success' => false, 'message' => 'Could not find item data octets in raw data'];
        }

        $actualOctetsOffset = $searchStart + $octetsPos;

        $result = substr($roleData, 0, $actualOctetsOffset);
        $result .= $newOctets;
        $result .= substr($roleData, $actualOctetsOffset + strlen($oldOctets));

        $putPack = pack('NNC', -1, $roleId, 1).$result;
        $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

        sleep(1);
        $verifyPocket = $this->getRoleInventory($roleId);
        foreach ($verifyPocket['inv'] as $item) {
            if (($item['id'] ?? 0) == $itemId && ($item['pos'] ?? -1) == $itemPos) {
                if (($item['data'] ?? '') === $newData) {
                    return [
                        'success' => true,
                        'message' => 'Item data updated successfully',
                        'item_id' => $itemId,
                        'item_pos' => $itemPos,
                    ];
                }
                break;
            }
        }

        return ['success' => false, 'message' => 'Update failed - character might be online'];
    }

    public function removeItems($roleId, $items)
    {
        $pocket = $this->getRoleInventory($roleId);
        if (! $pocket || ! isset($pocket['inv'])) {
            return ['success' => false, 'message' => 'Could not get inventory'];
        }

        $inventory = $pocket['inv'];
        $removed = [];

        foreach ($items as $itemToRemove) {
            $itemId = $itemToRemove['id'] ?? 0;
            $countToRemove = $itemToRemove['count'] ?? 1;

            if ($itemId <= 0) {
                continue;
            }

            foreach ($inventory as $idx => $invItem) {
                if (($invItem['id'] ?? 0) == $itemId) {
                    $currentCount = $invItem['count'] ?? 0;

                    if ($currentCount < $countToRemove) {
                        return [
                            'success' => false,
                            'message' => "Not enough items. Have: $currentCount, need: $countToRemove for item ID: $itemId",
                        ];
                    }

                    $newCount = $currentCount - $countToRemove;

                    if ($newCount <= 0) {
                        $inventory[$idx]['id'] = 0;
                        $inventory[$idx]['count'] = 0;
                    } else {
                        $inventory[$idx]['count'] = $newCount;
                    }

                    $removed[] = ['id' => $itemId, 'removed' => $countToRemove, 'remaining' => $newCount];
                    break;
                }
            }
        }

        if (empty($removed)) {
            return ['success' => false, 'message' => 'No items found to remove'];
        }

        $pocket['inv'] = $inventory;

        $base = $this->getRoleBase($roleId);
        $status = $this->getRoleStatus($roleId);
        $equipment = $this->getRoleEquipment($roleId);
        $storehouse = $this->getRoleStorehouse($roleId);
        $task = $this->getRoleTask($roleId);

        $fullRole = [
            'base' => $base,
            'status' => $status,
            'pocket' => $pocket,
            'equipment' => $equipment,
            'storehouse' => $storehouse,
            'task' => $task,
        ];

        $this->putRole($roleId, $fullRole);

        sleep(1);

        return [
            'success' => true,
            'message' => 'Items removed successfully',
            'removed' => $removed,
        ];
    }

    public function exchangeItemToGold($roleId, $itemId, $maxCount = null)
    {
        try {
            if ($this->checkRoleOnline($roleId)) {
                return [
                    'success' => false,
                    'message' => 'Character must be offline to exchange items',
                    'count' => 0,
                    'available' => 0,
                ];
            }

            $pocket = $this->getRoleInventory($roleId);

            if (! $pocket || ! isset($pocket['inv'])) {
                return [
                    'success' => false,
                    'message' => 'Failed to get inventory',
                    'count' => 0,
                    'available' => 0,
                ];
            }

            $inventory = $pocket['inv'];
            $itemsToDelete = [];
            $totalAvailable = 0;

            foreach ($inventory as $item) {
                if (($item['id'] ?? 0) == $itemId) {
                    $count = $item['count'] ?? 0;
                    $pos = $item['pos'] ?? -1;
                    if ($count > 0 && $pos >= 0) {
                        $itemsToDelete[] = [
                            'pos' => $pos,
                            'count' => $count,
                        ];
                        $totalAvailable += $count;
                    }
                }
            }

            if (empty($itemsToDelete)) {
                return [
                    'success' => false,
                    'message' => 'Item not found in inventory',
                    'count' => 0,
                    'available' => 0,
                ];
            }

            $targetCount = ($maxCount !== null && $maxCount > 0) ? min($maxCount, $totalAvailable) : $totalAvailable;

            if ($targetCount <= 0) {
                return [
                    'success' => false,
                    'message' => 'Invalid exchange amount',
                    'count' => 0,
                    'available' => $totalAvailable,
                ];
            }

            $deletedCount = 0;
            $remainingToDelete = $targetCount;

            foreach ($itemsToDelete as $itemInfo) {
                if ($remainingToDelete <= 0) {
                    break;
                }

                $itemCount = $itemInfo['count'];

                if ($itemCount <= $remainingToDelete) {
                    $result = $this->deleteItem($roleId, $itemInfo['pos'], 'pocket');
                    if ($result['success']) {
                        $deletedCount += $itemCount;
                        $remainingToDelete -= $itemCount;
                    }
                } else {
                    $newCount = $itemCount - $remainingToDelete;

                    $currentPocket = $this->getRoleInventory($roleId);
                    $currentItem = null;
                    foreach ($currentPocket['inv'] ?? [] as $ci) {
                        if (($ci['pos'] ?? -1) == $itemInfo['pos']) {
                            $currentItem = $ci;
                            break;
                        }
                    }

                    if ($currentItem) {
                        $currentItem['count'] = $newCount;
                        $result = $this->updateItem($roleId, $itemInfo['pos'], $currentItem, 'pocket');
                        if ($result['success']) {
                            $deletedCount += $remainingToDelete;
                            $remainingToDelete = 0;
                        }
                    }
                }
            }

            sleep(2);

            $verifyPocket = $this->getRoleInventory($roleId);
            $remainingCount = 0;

            foreach ($verifyPocket['inv'] ?? [] as $item) {
                if (($item['id'] ?? 0) == $itemId) {
                    $remainingCount += ($item['count'] ?? 0);
                }
            }

            $expectedRemaining = $totalAvailable - $deletedCount;
            $success = ($remainingCount == $expectedRemaining);

            return [
                'success' => $success,
                'message' => $success
                    ? 'Items successfully exchanged'
                    : 'Partial exchange - some items may remain',
                'count' => $deletedCount,
                'available' => $totalAvailable,
                'remaining' => $remainingCount,
                'deleted_stacks' => count($itemsToDelete),
                'needs_relog' => ! $success,
            ];

        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Exception: '.$e->getMessage(),
                'count' => 0,
                'available' => 0,
            ];
        }
    }

    public function updateRoleData($roleId, $data = [], $itemsToRemove = [])
    {
        $base = $this->getRoleBase($roleId);
        $status = $this->getRoleStatus($roleId);
        $pocket = $this->getRoleInventory($roleId);
        $equipment = $this->getRoleEquipment($roleId);
        $storehouse = $this->getRoleStorehouse($roleId);
        $task = $this->getRoleTask($roleId);

        if (! $base) {
            return ['success' => false, 'message' => 'Role not found'];
        }

        if (! empty($itemsToRemove) && isset($pocket['inv'])) {
            $inventory = $pocket['inv'];
            foreach ($itemsToRemove as $itemToRemove) {
                $itemId = $itemToRemove['id'] ?? 0;
                $countToRemove = $itemToRemove['count'] ?? 1;

                foreach ($inventory as $idx => $invItem) {
                    if (($invItem['id'] ?? 0) == $itemId) {
                        $currentCount = $invItem['count'] ?? 0;
                        $newCount = max(0, $currentCount - $countToRemove);

                        if ($newCount <= 0) {
                            $inventory[$idx]['id'] = 0;
                            $inventory[$idx]['count'] = 0;
                        } else {
                            $inventory[$idx]['count'] = $newCount;
                        }
                        break;
                    }
                }
            }
            $pocket['inv'] = $inventory;
        }

        if (isset($data['base'])) {
            foreach ($data['base'] as $key => $value) {
                $base[$key] = $value;
            }
        }
        if (isset($data['status'])) {
            foreach ($data['status'] as $key => $value) {
                $status[$key] = $value;
            }
        }
        if (isset($data['pocket'])) {
            foreach ($data['pocket'] as $key => $value) {
                $pocket[$key] = $value;
            }
        }
        if (isset($data['equipment'])) {
            foreach ($data['equipment'] as $key => $value) {
                $equipment[$key] = $value;
            }
        }
        if (isset($data['storehouse'])) {
            foreach ($data['storehouse'] as $key => $value) {
                $storehouse[$key] = $value;
            }
        }

        $fullRole = [
            'base' => $base,
            'status' => $status,
            'pocket' => $pocket,
            'equipment' => $equipment,
            'storehouse' => $storehouse,
            'task' => $task,
        ];

        $this->putRole($roleId, $fullRole);

        return [
            'success' => true,
            'message' => 'Role updated successfully',
        ];
    }

    public function addItem($roleId, $itemPos, $newItem, $section = 'pocket')
    {
        if ($section === 'pocket') {
            $inventory = $this->getRoleInventory($roleId);
            $items = $inventory['inv'] ?? [];
        } elseif ($section === 'equipment') {
            $inventory = $this->getRoleEquipment($roleId);
            $items = $inventory['eqp'] ?? [];
        } elseif ($section === 'storehouse') {
            $inventory = $this->getRoleStorehouse($roleId);
            $items = $inventory['store'] ?? [];
        } else {
            return ['success' => false, 'message' => 'Invalid section'];
        }

        if ($inventory === false || $inventory === null) {
            return ['success' => false, 'message' => "Could not get $section data"];
        }

        $newItemId = $newItem['id'] ?? 0;
        $newItemCount = $newItem['count'] ?? 1;
        $newItemMaxCount = $newItem['max_count'] ?? $newItemCount;
        $newItemData = $newItem['data'] ?? '';
        $newItemProctype = $newItem['proctype'] ?? 0;
        $newItemExpire = $newItem['expire_date'] ?? 0;
        $newItemGuid1 = $newItem['guid1'] ?? 0;
        $newItemGuid2 = $newItem['guid2'] ?? 0;
        $newItemMask = $newItem['mask'] ?? 0;

        $newItemDataBin = ! empty($newItemData) ? pack('H*', $newItemData) : '';

        $newItemBin = pack('NNNN', $newItemId, $itemPos, $newItemCount, $newItemMaxCount);
        $newItemBin .= $this->gamed->cuint(strlen($newItemDataBin)).$newItemDataBin;
        $newItemBin .= pack('NNNNN', $newItemProctype, $newItemExpire, $newItemGuid1, $newItemGuid2, $newItemMask);

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 1000) {
            return ['success' => false, 'message' => 'Failed to get raw role data'];
        }

        $currentItemCount = count($items);

        if ($currentItemCount > 0) {
            $firstItem = $items[0];
            $firstItemPattern = pack('NN', $firstItem['id'], $firstItem['pos']);
            $firstItemOffset = strpos($roleData, $firstItemPattern);

            if ($firstItemOffset === false) {
                return ['success' => false, 'message' => 'Could not find first item in raw data'];
            }

            $itemcountOffset = $firstItemOffset;
            for ($i = 1; $i <= 5; $i++) {
                $tmp = 0;
                $testData = substr($roleData, $firstItemOffset - $i, $i);
                $decoded = $this->gamed->unpackCuint($testData, $tmp);
                if ($decoded == $currentItemCount && $tmp == $i) {
                    $itemcountOffset = $firstItemOffset - $i;
                    break;
                }
            }

            $lastItem = end($items);
            $lastItemPattern = pack('NNNN', $lastItem['id'], $lastItem['pos'], $lastItem['count'], $lastItem['max_count'] ?? 1);
            $lastItemOffset = strpos($roleData, $lastItemPattern);

            if ($lastItemOffset === false) {
                return ['success' => false, 'message' => 'Could not find last item in raw data'];
            }

            $lastItemData = $lastItem['data'] ?? '';
            $lastItemDataBin = ! empty($lastItemData) ? pack('H*', $lastItemData) : '';
            $lastItemOctets = $this->gamed->cuint(strlen($lastItemDataBin)).$lastItemDataBin;
            $lastItemSize = 16 + strlen($lastItemOctets) + 20;

            $insertOffset = $lastItemOffset + $lastItemSize;

            $newRoleData = substr($roleData, 0, $itemcountOffset);
            $newRoleData .= $this->gamed->cuint($currentItemCount + 1);
            $newRoleData .= substr($roleData, $firstItemOffset, $insertOffset - $firstItemOffset);
            $newRoleData .= $newItemBin;
            $newRoleData .= substr($roleData, $insertOffset);
        } else {
            if ($section === 'equipment') {
                $pocket = $this->getRoleInventory($roleId);
                $pocketItems = $pocket['inv'] ?? [];

                $itemcountOffset = null;

                if (! empty($pocketItems)) {
                    $lastPocketItem = end($pocketItems);
                    $lastPocketPattern = pack('NNNN', $lastPocketItem['id'], $lastPocketItem['pos'],
                        $lastPocketItem['count'], $lastPocketItem['max_count'] ?? 1);
                    $lastPocketOffset = strpos($roleData, $lastPocketPattern);

                    if ($lastPocketOffset !== false) {
                        $lastPocketData = $lastPocketItem['data'] ?? '';
                        $lastPocketDataBin = ! empty($lastPocketData) ? pack('H*', $lastPocketData) : '';
                        $lastPocketOctets = $this->gamed->cuint(strlen($lastPocketDataBin)).$lastPocketDataBin;
                        $lastPocketSize = 16 + strlen($lastPocketOctets) + 20;

                        $itemcountOffset = $lastPocketOffset + $lastPocketSize + 8;
                    }
                }

                if ($itemcountOffset === null) {
                    $pocketCapacity = $pocket['icapacity'] ?? 0;
                    $pocketTimestamp = $pocket['timestamp'] ?? 0;
                    $pocketMoney = $pocket['money'] ?? 0;

                    $searchPattern = pack('NNN', $pocketCapacity, $pocketTimestamp, $pocketMoney);
                    $patternOffset = strpos($roleData, $searchPattern);

                    if ($patternOffset !== false) {
                        $pocketMoneyOffset = $patternOffset + 12;
                        $cuintLen = 1;
                        $tmp = 0;
                        $testData = substr($roleData, $pocketMoneyOffset, 2);
                        $decoded = $this->gamed->unpackCuint($testData, $tmp);
                        $cuintLen = $tmp > 0 ? $tmp : 1;
                        $itemcountOffset = $pocketMoneyOffset + $cuintLen + 8;
                    }
                }

                if ($itemcountOffset === null) {
                    return ['success' => false, 'message' => 'Could not find equipment section'];
                }

                $newRoleData = substr($roleData, 0, $itemcountOffset);
                $newRoleData .= $this->gamed->cuint(1);
                $newRoleData .= $newItemBin;
                $newRoleData .= substr($roleData, $itemcountOffset + 1);
            } elseif ($section === 'storehouse') {
                $storeCapacity = $inventory['capacity'] ?? 16;
                $storeMoney = $inventory['money'] ?? 0;

                $searchPattern = pack('NN', $storeCapacity, $storeMoney).chr(0);
                $patternOffset = strpos($roleData, $searchPattern);

                if ($patternOffset !== false) {
                    $itemcountOffset = $patternOffset + 8;
                    $newRoleData = substr($roleData, 0, $itemcountOffset);
                    $newRoleData .= $this->gamed->cuint(1);
                    $newRoleData .= $newItemBin;
                    $newRoleData .= substr($roleData, $itemcountOffset + 1);
                } else {
                    return ['success' => false, 'message' => 'Could not find storehouse section marker'];
                }
            } else {
                $pocketCapacity = $inventory['icapacity'] ?? 32;
                $pocketTimestamp = $inventory['timestamp'] ?? 0;
                $pocketMoney = $inventory['money'] ?? 0;

                $searchPattern = pack('NNN', $pocketCapacity, $pocketTimestamp, $pocketMoney).chr(0);
                $patternOffset = strpos($roleData, $searchPattern);

                if ($patternOffset !== false) {
                    $itemcountOffset = $patternOffset + 12;
                    $newRoleData = substr($roleData, 0, $itemcountOffset);
                    $newRoleData .= $this->gamed->cuint(1);
                    $newRoleData .= $newItemBin;
                    $newRoleData .= substr($roleData, $itemcountOffset + 1);
                } else {
                    return ['success' => false, 'message' => 'Could not find empty pocket section marker'];
                }
            }
        }

        if ($section === 'storehouse') {
            $storehouse = $this->getRoleStorehouse($roleId);
            if ($storehouse === false || $storehouse === null) {
                return ['success' => false, 'message' => 'Could not get storehouse data'];
            }

            $existingStore = $storehouse['store'] ?? [];

            // Check if position already occupied
            foreach ($existingStore as $item) {
                if (($item['pos'] ?? -1) == $itemPos && ($item['id'] ?? 0) > 0) {
                    return ['success' => false, 'message' => "Position $itemPos already occupied in storehouse"];
                }
            }

            $existingStore[] = [
                'id' => $newItemId,
                'pos' => $itemPos,
                'count' => $newItemCount,
                'max_count' => $newItemMaxCount,
                'data' => $newItemData,
                'proctype' => $newItemProctype,
                'expire_date' => $newItemExpire,
                'guid1' => $newItemGuid1,
                'guid2' => $newItemGuid2,
                'mask' => $newItemMask,
            ];

            // Rebuild storehouse in correct protocol order
            $newStorehouse = [
                'capacity' => $storehouse['capacity'] ?? 128,
                'money' => $storehouse['money'] ?? 0,
                'storecount' => count($existingStore),
                'store' => $existingStore,
                'size1' => $storehouse['size1'] ?? 128,
                'size2' => $storehouse['size2'] ?? 128,
                'dresscount' => $storehouse['dresscount'] ?? 0,
                'dress' => $storehouse['dress'] ?? [],
                'materialcount' => $storehouse['materialcount'] ?? count($storehouse['material'] ?? []),
                'material' => $storehouse['material'] ?? [],
                'size3' => $storehouse['size3'] ?? 120,
                'generalcardcount' => $storehouse['generalcardcount'] ?? count($storehouse['generalcard'] ?? []),
                'generalcard' => $storehouse['generalcard'] ?? [],
                'reserved' => $storehouse['reserved'] ?? 0,
            ];

            // Get full role data and update storehouse section
            $base = $this->getRoleBase($roleId);
            $status = $this->getRoleStatus($roleId);
            $pocket = $this->getRoleInventory($roleId);
            $equipment = $this->getRoleEquipment($roleId);
            $task = $this->getRoleTask($roleId);

            if (! $base) {
                return ['success' => false, 'message' => 'Failed to get role data'];
            }

            $fullRole = [
                'base' => $base,
                'status' => $status,
                'pocket' => $pocket,
                'equipment' => $equipment,
                'storehouse' => $newStorehouse,
                'task' => $task,
            ];

            $this->putRole($roleId, $fullRole);
        } else {
            $putPack = pack('NNC', -1, $roleId, 1).$newRoleData;
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));
        }

        sleep(1);
        if ($section === 'pocket') {
            $verifyInv = $this->getRoleInventory($roleId);
            $verifyItems = $verifyInv['inv'] ?? [];
        } elseif ($section === 'equipment') {
            $verifyInv = $this->getRoleEquipment($roleId);
            $verifyItems = $verifyInv['eqp'] ?? [];
        } else {
            $verifyInv = $this->getRoleStorehouse($roleId);
            $verifyItems = $verifyInv['store'] ?? [];
        }

        foreach ($verifyItems as $item) {
            if (($item['pos'] ?? -1) == $itemPos && ($item['id'] ?? 0) == $newItemId) {
                return [
                    'success' => true,
                    'message' => "Item added to $section successfully",
                    'item_pos' => $itemPos,
                ];
            }
        }

        return ['success' => false, 'message' => 'Add item failed - character might be online'];
    }

    public function deleteItem($roleId, $itemPos, $section = 'pocket')
    {
        if ($section === 'pocket') {
            $inventory = $this->getRoleInventory($roleId);
            $items = $inventory['inv'] ?? [];
            $money = $inventory['money'] ?? 0;
        } elseif ($section === 'equipment') {
            $inventory = $this->getRoleEquipment($roleId);
            $items = $inventory['eqp'] ?? [];
            $money = 0;
        } elseif ($section === 'storehouse') {
            $inventory = $this->getRoleStorehouse($roleId);
            $items = $inventory['store'] ?? [];
            $money = $inventory['money'] ?? 0;
            $capacity = $inventory['capacity'] ?? 16;
        } else {
            return ['success' => false, 'message' => 'Invalid section'];
        }

        $itemToDelete = null;
        foreach ($items as $item) {
            if (($item['pos'] ?? -1) == $itemPos) {
                $itemToDelete = $item;
                break;
            }
        }

        if (! $itemToDelete) {
            return ['success' => false, 'message' => "Item not found at position $itemPos"];
        }

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 500) {
            return ['success' => false, 'message' => 'Failed to get role data'];
        }

        $sectionStart = null;
        $itemCount = count($items);

        if ($section === 'pocket') {
            if (! empty($items)) {
                $firstItem = reset($items);
                $firstItemPattern = pack('NN', $firstItem['id'], $firstItem['pos']);
                $firstItemOffset = strpos($roleData, $firstItemPattern);
                if ($firstItemOffset !== false) {
                    $cuintLen = $itemCount < 128 ? 1 : 2;
                    $sectionStart = $firstItemOffset - $cuintLen - 4;
                }
            }
        } elseif ($section === 'equipment') {
            $pocket = $this->getRoleInventory($roleId);
            $pocketItems = $pocket['inv'] ?? [];
            if (! empty($pocketItems)) {
                $lastPocketItem = end($pocketItems);
                $lastPocketPattern = pack('NNNN', $lastPocketItem['id'], $lastPocketItem['pos'],
                    $lastPocketItem['count'], $lastPocketItem['max_count'] ?? 1);
                $lastPocketOffset = strpos($roleData, $lastPocketPattern);
                if ($lastPocketOffset !== false) {
                    $lastPocketData = $lastPocketItem['data'] ?? '';
                    $lastPocketDataBin = ! empty($lastPocketData) ? pack('H*', $lastPocketData) : '';
                    $lastPocketOctets = $this->gamed->cuint(strlen($lastPocketDataBin)).$lastPocketDataBin;
                    $lastPocketSize = 16 + strlen($lastPocketOctets) + 20;
                    $sectionStart = $lastPocketOffset + $lastPocketSize + 8;
                }
            }

            if ($sectionStart === null && ! empty($items)) {
                $firstItem = reset($items);
                $firstItemPattern = pack('NNNN', $firstItem['id'], $firstItem['pos'],
                    $firstItem['count'], $firstItem['max_count'] ?? 1);
                $firstItemOffset = strpos($roleData, $firstItemPattern);
                if ($firstItemOffset !== false) {
                    $itemcountOffset = $firstItemOffset;
                    for ($i = 1; $i <= 5; $i++) {
                        $tmp = 0;
                        $testData = substr($roleData, $firstItemOffset - $i, $i);
                        $decoded = $this->gamed->unpackCuint($testData, $tmp);
                        if ($decoded == $itemCount && $tmp == $i) {
                            $itemcountOffset = $firstItemOffset - $i;
                            break;
                        }
                    }
                    $sectionStart = $itemcountOffset;
                }
            }
        } elseif ($section === 'storehouse') {
            $storePattern = pack('NN', $capacity, $money);
            $offset = 0;
            while (($pos = strpos($roleData, $storePattern, $offset)) !== false) {
                $countByte = ord($roleData[$pos + 8]);
                if ($countByte == $itemCount) {
                    $sectionStart = $pos;
                    break;
                }
                $offset = $pos + 1;
            }
        }

        if ($sectionStart === null) {
            return ['success' => false, 'message' => 'Could not find section start'];
        }

        $deleteItemOffset = false;

        $deleteItemPattern = pack('NNNN', $itemToDelete['id'], $itemToDelete['pos'], $itemToDelete['count'], $itemToDelete['max_count'] ?? 1);
        $deleteItemOffset = strpos($roleData, $deleteItemPattern, $sectionStart);

        if ($deleteItemOffset === false) {
            $deleteItemPattern = pack('NN', $itemToDelete['id'], $itemToDelete['pos']);
            $deleteItemOffset = strpos($roleData, $deleteItemPattern, $sectionStart);
        }

        if ($deleteItemOffset === false) {
            $currentOffset = $sectionStart;
            for ($i = 0; $i < $itemCount; $i++) {
                $itemStart = $currentOffset;
                $itemIdBytes = substr($roleData, $currentOffset, 8);
                if (strlen($itemIdBytes) < 8) {
                    break;
                }
                $headerData = unpack('N2', $itemIdBytes);
                $currentItemPos = $headerData[2] ?? -1;

                if ($currentItemPos == $itemPos) {
                    $deleteItemOffset = $itemStart;
                    break;
                }

                $currentOffset += 16;
                $dataLen = ord($roleData[$currentOffset]);
                if ($dataLen >= 128) {
                    $dataLen = (($dataLen & 0x7F) << 8) | ord($roleData[$currentOffset + 1]);
                    $currentOffset += 2 + $dataLen;
                } else {
                    $currentOffset += 1 + $dataLen;
                }
                $currentOffset += 20;
            }
        }

        if ($deleteItemOffset === false) {
            return ['success' => false, 'message' => 'Could not find item in raw data (pos: '.$itemPos.')'];
        }

        $currentOffset = $sectionStart;
        if ($section === 'pocket') {
            $cuintLen = $itemCount < 128 ? 1 : 2;
            $currentOffset += 4 + $cuintLen;
        } elseif ($section === 'equipment') {
            $cuintLen = $itemCount < 128 ? 1 : 2;
            $currentOffset += $cuintLen;
        } elseif ($section === 'storehouse') {
            $cuintLen = $itemCount < 128 ? 1 : 2;
            $currentOffset += 8 + $cuintLen;
        }

        for ($i = 0; $i < $itemCount; $i++) {
            $currentOffset += 16;
            $dataLen = ord($roleData[$currentOffset]);
            if ($dataLen >= 128) {
                $dataLen = (($dataLen & 0x7F) << 8) | ord($roleData[$currentOffset + 1]);
                $currentOffset += 2 + $dataLen;
            } else {
                $currentOffset += 1 + $dataLen;
            }
            $currentOffset += 20;
        }
        $sectionEnd = $currentOffset;

        $newItemCount = $itemCount - 1;
        $newCountCuint = $this->gamed->cuint($newItemCount);

        if ($section === 'pocket') {
            $newSection = pack('N', $money).$newCountCuint;
            $headerLen = 4 + ($itemCount < 128 ? 1 : 2);
        } elseif ($section === 'equipment') {
            $newSection = $newCountCuint;
            $headerLen = $itemCount < 128 ? 1 : 2;
        } elseif ($section === 'storehouse') {
            $newSection = pack('NN', $capacity, $money).$newCountCuint;
            $headerLen = 8 + ($itemCount < 128 ? 1 : 2);
        }

        $currentOffset = $sectionStart + $headerLen;
        for ($i = 0; $i < $itemCount; $i++) {
            $itemStart = $currentOffset;
            $currentOffset += 16;
            $dataLen = ord($roleData[$currentOffset]);
            if ($dataLen >= 128) {
                $dataLen = (($dataLen & 0x7F) << 8) | ord($roleData[$currentOffset + 1]);
                $currentOffset += 2 + $dataLen;
            } else {
                $currentOffset += 1 + $dataLen;
            }
            $currentOffset += 20;

            if ($itemStart == $deleteItemOffset) {
                continue;
            }

            $newSection .= substr($roleData, $itemStart, $currentOffset - $itemStart);
        }

        if ($section === 'storehouse') {
            $storehouse = $this->getRoleStorehouse($roleId);
            if (! $storehouse) {
                return ['success' => false, 'message' => 'Could not get storehouse data'];
            }

            $storeItems = $storehouse['store'] ?? [];
            $newStoreItems = [];
            foreach ($storeItems as $item) {
                if (($item['pos'] ?? -1) != $itemPos) {
                    $newStoreItems[] = $item;
                }
            }

            // Rebuild storehouse in correct protocol order
            $newStorehouse = [
                'capacity' => $storehouse['capacity'] ?? 128,
                'money' => $storehouse['money'] ?? 0,
                'storecount' => count($newStoreItems),
                'store' => $newStoreItems,
                'size1' => $storehouse['size1'] ?? 128,
                'size2' => $storehouse['size2'] ?? 128,
                'dresscount' => $storehouse['dresscount'] ?? 0,
                'dress' => $storehouse['dress'] ?? [],
                'materialcount' => $storehouse['materialcount'] ?? count($storehouse['material'] ?? []),
                'material' => $storehouse['material'] ?? [],
                'size3' => $storehouse['size3'] ?? 120,
                'generalcardcount' => $storehouse['generalcardcount'] ?? count($storehouse['generalcard'] ?? []),
                'generalcard' => $storehouse['generalcard'] ?? [],
                'reserved' => $storehouse['reserved'] ?? 0,
            ];

            // Get full role data and update storehouse section
            $base = $this->getRoleBase($roleId);
            $status = $this->getRoleStatus($roleId);
            $pocket = $this->getRoleInventory($roleId);
            $equipment = $this->getRoleEquipment($roleId);
            $task = $this->getRoleTask($roleId);

            if (! $base) {
                return ['success' => false, 'message' => 'Failed to get role data'];
            }

            $fullRole = [
                'base' => $base,
                'status' => $status,
                'pocket' => $pocket,
                'equipment' => $equipment,
                'storehouse' => $newStorehouse,
                'task' => $task,
            ];

            $this->putRole($roleId, $fullRole);
        } else {
            $newRoleData = substr($roleData, 0, $sectionStart).$newSection.substr($roleData, $sectionEnd);
            $putPack = pack('NNC', -1, $roleId, 1).$newRoleData;
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));
        }

        sleep(1);

        if ($section === 'pocket') {
            $verifyInv = $this->getRoleInventory($roleId);
            $verifyItems = $verifyInv['inv'] ?? [];
        } elseif ($section === 'equipment') {
            $verifyInv = $this->getRoleEquipment($roleId);
            $verifyItems = $verifyInv['eqp'] ?? [];
        } else {
            $verifyInv = $this->getRoleStorehouse($roleId);
            $verifyItems = $verifyInv['store'] ?? [];
        }

        foreach ($verifyItems as $item) {
            if (($item['pos'] ?? -1) == $itemPos) {
                return ['success' => false, 'message' => 'Delete failed - item still exists'];
            }
        }

        if (count($verifyItems) == $newItemCount) {
            return [
                'success' => true,
                'message' => "Item deleted from $section successfully",
                'deleted_pos' => $itemPos,
                'remaining_items' => $newItemCount,
            ];
        }

        return ['success' => false, 'message' => 'Delete failed - character might be online'];
    }

    public function updateItem($roleId, $itemPos, $newItem, $section = 'pocket')
    {
        if ($section === 'pocket') {
            $inventory = $this->getRoleInventory($roleId);
            $items = $inventory['inv'] ?? [];
        } elseif ($section === 'equipment') {
            $inventory = $this->getRoleEquipment($roleId);
            $items = $inventory['eqp'] ?? [];
        } elseif ($section === 'storehouse') {
            $inventory = $this->getRoleStorehouse($roleId);
            $items = $inventory['store'] ?? [];
        } else {
            return ['success' => false, 'message' => 'Invalid section'];
        }

        if (! $inventory) {
            return ['success' => false, 'message' => "Could not get $section data"];
        }

        $currentItem = null;
        foreach ($items as $item) {
            if (($item['pos'] ?? -1) == $itemPos) {
                $currentItem = $item;
                break;
            }
        }

        if (! $currentItem) {
            return ['success' => false, 'message' => "Item at pos $itemPos not found in $section"];
        }

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 1000) {
            return ['success' => false, 'message' => 'Failed to get raw role data'];
        }

        $oldId = $currentItem['id'] ?? 0;
        $oldPos = $currentItem['pos'] ?? 0;
        $oldCount = $currentItem['count'] ?? 0;
        $oldMaxCount = $currentItem['max_count'] ?? 1;
        $oldData = $currentItem['data'] ?? '';
        $oldProctype = $currentItem['proctype'] ?? 0;
        $oldExpireDate = $currentItem['expire_date'] ?? 0;
        $oldGuid1 = $currentItem['guid1'] ?? 0;
        $oldGuid2 = $currentItem['guid2'] ?? 0;
        $oldMask = $currentItem['mask'] ?? 0;

        $oldDataBin = ! empty($oldData) ? pack('H*', $oldData) : '';
        $oldDataOctets = $this->gamed->cuint(strlen($oldDataBin)).$oldDataBin;

        $oldItemBin = pack('NNNN', $oldId, $oldPos, $oldCount, $oldMaxCount);
        $oldItemBin .= $oldDataOctets;
        $oldItemBin .= pack('NNNNN', $oldProctype, $oldExpireDate, $oldGuid1, $oldGuid2, $oldMask);

        $itemOffset = strpos($roleData, $oldItemBin);

        if ($itemOffset === false) {
            $searchPattern = pack('NNNN', $oldId, $oldPos, $oldCount, $oldMaxCount);
            $itemOffset = strpos($roleData, $searchPattern);

            if ($itemOffset === false) {
                return ['success' => false, 'message' => 'Could not find item in raw data'];
            }

            $dataLenOffset = $itemOffset + 16;
            $tmp = 0;
            $dataLen = $this->gamed->unpackCuint(substr($roleData, $dataLenOffset, 5), $tmp);
            $oldItemLen = 16 + $tmp + $dataLen + 20;
            $oldItemBin = substr($roleData, $itemOffset, $oldItemLen);
        }

        $newId = $newItem['id'] ?? $oldId;
        $newPos = $newItem['pos'] ?? $oldPos;
        $newCount = $newItem['count'] ?? $oldCount;
        $newMaxCount = $newItem['max_count'] ?? $oldMaxCount;
        $newData = array_key_exists('data', $newItem) ? $newItem['data'] : $oldData;
        $newProctype = $newItem['proctype'] ?? $oldProctype;
        $newExpireDate = $newItem['expire_date'] ?? $oldExpireDate;
        $newGuid1 = $newItem['guid1'] ?? $oldGuid1;
        $newGuid2 = $newItem['guid2'] ?? $oldGuid2;
        $newMask = $newItem['mask'] ?? $oldMask;

        $newDataBin = ! empty($newData) ? pack('H*', $newData) : '';
        $newDataOctets = $this->gamed->cuint(strlen($newDataBin)).$newDataBin;

        $newItemBin = pack('NNNN', $newId, $newPos, $newCount, $newMaxCount);
        $newItemBin .= $newDataOctets;
        $newItemBin .= pack('NNNNN', $newProctype, $newExpireDate, $newGuid1, $newGuid2, $newMask);

        if ($section === 'storehouse') {
            $storehouse = $this->getRoleStorehouse($roleId);
            if (! $storehouse) {
                return ['success' => false, 'message' => 'Could not get storehouse data'];
            }

            $storeItems = $storehouse['store'] ?? [];
            $updated = false;
            foreach ($storeItems as &$item) {
                if (($item['pos'] ?? -1) == $itemPos) {
                    $item['id'] = $newId;
                    $item['pos'] = $newPos;
                    $item['count'] = $newCount;
                    $item['max_count'] = $newMaxCount;
                    if (array_key_exists('data', $newItem)) {
                        $item['data'] = $newData;
                    }
                    $item['proctype'] = $newProctype;
                    $item['expire_date'] = $newExpireDate;
                    $item['guid1'] = $newGuid1;
                    $item['guid2'] = $newGuid2;
                    $item['mask'] = $newMask;
                    $updated = true;
                    break;
                }
            }
            unset($item);

            if (! $updated) {
                return ['success' => false, 'message' => "Item at pos $itemPos not found in storehouse"];
            }

            // Rebuild storehouse in correct protocol order
            $newStorehouse = [
                'capacity' => $storehouse['capacity'] ?? 128,
                'money' => $storehouse['money'] ?? 0,
                'storecount' => count($storeItems),
                'store' => $storeItems,
                'size1' => $storehouse['size1'] ?? 128,
                'size2' => $storehouse['size2'] ?? 128,
                'dresscount' => $storehouse['dresscount'] ?? 0,
                'dress' => $storehouse['dress'] ?? [],
                'materialcount' => $storehouse['materialcount'] ?? count($storehouse['material'] ?? []),
                'material' => $storehouse['material'] ?? [],
                'size3' => $storehouse['size3'] ?? 120,
                'generalcardcount' => $storehouse['generalcardcount'] ?? count($storehouse['generalcard'] ?? []),
                'generalcard' => $storehouse['generalcard'] ?? [],
                'reserved' => $storehouse['reserved'] ?? 0,
            ];

            // Get full role data and update storehouse section
            $base = $this->getRoleBase($roleId);
            $status = $this->getRoleStatus($roleId);
            $pocket = $this->getRoleInventory($roleId);
            $equipment = $this->getRoleEquipment($roleId);
            $task = $this->getRoleTask($roleId);

            if (! $base) {
                return ['success' => false, 'message' => 'Failed to get role data'];
            }

            $fullRole = [
                'base' => $base,
                'status' => $status,
                'pocket' => $pocket,
                'equipment' => $equipment,
                'storehouse' => $newStorehouse,
                'task' => $task,
            ];

            $this->putRole($roleId, $fullRole);

            return [
                'success' => true,
                'message' => "Item in $section updated successfully",
                'item_pos' => $newPos,
                'updated_fields' => array_keys($newItem),
            ];
        }

        $result = substr($roleData, 0, $itemOffset);
        $result .= $newItemBin;
        $result .= substr($roleData, $itemOffset + strlen($oldItemBin));

        $putPack = pack('NNC', -1, $roleId, 1).$result;
        $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

        return [
            'success' => true,
            'message' => "Item in $section updated successfully",
            'item_pos' => $newPos,
            'updated_fields' => array_keys($newItem),
        ];
    }

    public function findPocketStart($rawData, $items, $pocket = null)
    {
        if (! $rawData) {
            return null;
        }

        if (! empty($items)) {
            $firstItem = reset($items);
            if (isset($firstItem['id']) && isset($firstItem['pos']) && isset($firstItem['count'])) {
                $itemPattern = pack('NNNN', $firstItem['id'], $firstItem['pos'], $firstItem['count'], $firstItem['max_count'] ?? 1);
                $itemOffset = strpos($rawData, $itemPattern);

                if ($itemOffset !== false) {
                    $itemCount = count($items);
                    $cuintLen = $itemCount < 128 ? 1 : 2;
                    $pocketStart = $itemOffset - $cuintLen - 12;

                    if ($pocketStart >= 0) {
                        return $pocketStart;
                    }
                }
            }
        }

        if ($pocket !== null && isset($pocket['icapacity']) && isset($pocket['money'])) {
            $icapacity = (int) ($pocket['icapacity'] ?? 128);
            $money = (int) ($pocket['money'] ?? 0);

            $icapacities = [$icapacity, 32, 64, 128];
            foreach ($icapacities as $cap) {
                $moneyPattern = pack('N', $money)."\x00";
                $searchOffset = 0;

                while (($pos = strpos($rawData, $moneyPattern, $searchOffset)) !== false) {
                    $pocketStart = $pos - 8;
                    if ($pocketStart >= 0) {
                        $capCheck = unpack('N', substr($rawData, $pocketStart, 4))[1];
                        if ($capCheck == $cap) {
                            return $pocketStart;
                        }
                    }
                    $searchOffset = $pos + 1;
                }
            }
        }

        return null;
    }

    public function findPocketEnd($roleData, $pocketStart, $items)
    {
        $offset = $pocketStart + 4;

        $itemCount = ord($roleData[$offset]);
        if ($itemCount >= 128) {
            $itemCount = (($itemCount & 0x7F) << 8) | ord($roleData[$offset + 1]);
            $offset += 2;
        } else {
            $offset += 1;
        }

        for ($i = 0; $i < $itemCount; $i++) {
            $offset += 16;

            $dataLen = ord($roleData[$offset]);
            if ($dataLen >= 128) {
                $dataLen = (($dataLen & 0x7F) << 8) | ord($roleData[$offset + 1]);
                $offset += 2 + $dataLen;
            } else {
                $offset += 1 + $dataLen;
            }

            $offset += 20;
        }

        return $offset;
    }

    public function calculatePocketEnd($roleData, $pocketStart)
    {
        $dataLen = strlen($roleData);

        if ($pocketStart === null || $pocketStart < 0 || $pocketStart >= $dataLen) {
            return null;
        }

        $offset = $pocketStart + 12;

        if ($offset >= $dataLen) {
            return null;
        }

        $itemCount = ord($roleData[$offset]);
        if ($itemCount >= 128) {
            if ($offset + 1 >= $dataLen) {
                return null;
            }
            $itemCount = (($itemCount & 0x7F) << 8) | ord($roleData[$offset + 1]);
            $offset += 2;
        } else {
            $offset += 1;
        }

        for ($i = 0; $i < $itemCount; $i++) {
            $offset += 16;

            if ($offset >= $dataLen) {
                return null;
            }

            $octetLen = ord($roleData[$offset]);
            if ($octetLen >= 128) {
                if ($offset + 1 >= $dataLen) {
                    return null;
                }
                $octetLen = (($octetLen & 0x7F) << 8) | ord($roleData[$offset + 1]);
                $offset += 2 + $octetLen;
            } else {
                $offset += 1 + $octetLen;
            }

            $offset += 20;

            if ($offset > $dataLen) {
                return null;
            }
        }

        $offset += 8;

        return $offset;
    }

    public function updateStorehouseItemCount($roleId, $itemId, $newCount, $itemPos = null)
    {
        $storehouse = $this->getRoleStorehouse($roleId);
        if (! $storehouse || ! isset($storehouse['store'])) {
            return ['success' => false, 'message' => 'Could not get storehouse'];
        }

        $targetItem = null;
        foreach ($storehouse['store'] as $item) {
            if (($item['id'] ?? 0) == $itemId) {
                if ($itemPos !== null && ($item['pos'] ?? -1) != $itemPos) {
                    continue;
                }
                $targetItem = $item;
                break;
            }
        }

        if (! $targetItem) {
            return ['success' => false, 'message' => "Item ID $itemId not found in storehouse"];
        }

        $oldCount = $targetItem['count'] ?? 0;
        $targetPos = $targetItem['pos'] ?? 0;

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 1000) {
            return ['success' => false, 'message' => 'Failed to get raw role data'];
        }

        $itemIdPacked = pack('N', $itemId);
        $posPacked = pack('N', $targetPos);
        $oldCountPacked = pack('N', $oldCount);

        $searchPattern = $itemIdPacked.$posPacked.$oldCountPacked;
        $offset = strpos($roleData, $searchPattern);

        if ($offset === false) {
            $searchPattern2 = $itemIdPacked.$posPacked;
            $offset = strpos($roleData, $searchPattern2);

            if ($offset === false) {
                return ['success' => false, 'message' => 'Could not find item in raw data'];
            }
        }

        $countOffset = $offset + 8;

        $newCountPacked = pack('N', $newCount);
        $newData = substr($roleData, 0, $countOffset);
        $newData .= $newCountPacked;
        $newData .= substr($roleData, $countOffset + 4);

        $putPack = pack('NNC', -1, $roleId, 1).$newData;
        $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

        sleep(1);
        $verifyStore = $this->getRoleStorehouse($roleId);
        foreach ($verifyStore['store'] as $item) {
            if (($item['id'] ?? 0) == $itemId && ($item['pos'] ?? -1) == $targetPos) {
                if (($item['count'] ?? 0) == $newCount) {
                    return [
                        'success' => true,
                        'message' => 'Storehouse item count updated successfully',
                        'item_id' => $itemId,
                        'old_count' => $oldCount,
                        'new_count' => $newCount,
                    ];
                }
                break;
            }
        }

        return ['success' => false, 'message' => 'Update failed - character might be online'];
    }

    public function updateStorehouseItemData($roleId, $itemId, $itemPos, $newData)
    {
        $storehouse = $this->getRoleStorehouse($roleId);
        if (! $storehouse || ! isset($storehouse['store'])) {
            return ['success' => false, 'message' => 'Could not get storehouse'];
        }

        $targetItem = null;
        foreach ($storehouse['store'] as $item) {
            if (($item['id'] ?? 0) == $itemId && ($item['pos'] ?? -1) == $itemPos) {
                $targetItem = $item;
                break;
            }
        }

        if (! $targetItem) {
            return ['success' => false, 'message' => "Item ID $itemId at pos $itemPos not found in storehouse"];
        }

        $oldData = $targetItem['data'] ?? '';

        if ($oldData === $newData) {
            return ['success' => true, 'message' => 'Item data unchanged'];
        }

        $pack = pack('N*', -1, $roleId);
        $response = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $roleData = $this->gamed->deleteHeader($response);

        if (! $roleData || strlen($roleData) < 1000) {
            return ['success' => false, 'message' => 'Failed to get raw role data'];
        }

        $oldDataBin = ! empty($oldData) ? pack('H*', $oldData) : '';
        $newDataBin = ! empty($newData) ? pack('H*', $newData) : '';

        $itemIdPacked = pack('N', $itemId);
        $posPacked = pack('N', $itemPos);
        $countPacked = pack('N', $targetItem['count'] ?? 0);
        $maxCountPacked = pack('N', $targetItem['max_count'] ?? 1);

        $searchPattern = $itemIdPacked.$posPacked.$countPacked.$maxCountPacked;
        $itemOffset = strpos($roleData, $searchPattern);

        if ($itemOffset === false) {
            $searchPattern = $itemIdPacked.$posPacked.$countPacked;
            $itemOffset = strpos($roleData, $searchPattern);
        }

        if ($itemOffset === false) {
            return ['success' => false, 'message' => 'Could not find item in raw data'];
        }

        $dataOffset = $itemOffset + 16;

        $oldDataLen = strlen($oldDataBin);
        $oldOctets = $this->gamed->cuint($oldDataLen).$oldDataBin;

        $newDataLen = strlen($newDataBin);
        $newOctets = $this->gamed->cuint($newDataLen).$newDataBin;

        $searchStart = max(0, $dataOffset - 10);
        $searchEnd = min(strlen($roleData), $dataOffset + 100);
        $searchArea = substr($roleData, $searchStart, $searchEnd - $searchStart);

        $octetsPos = strpos($searchArea, $oldOctets);
        if ($octetsPos === false && $oldDataLen == 0) {
            $emptyOctet = $this->gamed->cuint(0);
            $octetsPos = strpos($searchArea, $emptyOctet);
        }

        if ($octetsPos === false) {
            return ['success' => false, 'message' => 'Could not find item data octets in raw data'];
        }

        $actualOctetsOffset = $searchStart + $octetsPos;

        $result = substr($roleData, 0, $actualOctetsOffset);
        $result .= $newOctets;
        $result .= substr($roleData, $actualOctetsOffset + strlen($oldOctets));

        $putPack = pack('NNC', -1, $roleId, 1).$result;
        $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

        sleep(1);
        $verifyStore = $this->getRoleStorehouse($roleId);
        foreach ($verifyStore['store'] as $item) {
            if (($item['id'] ?? 0) == $itemId && ($item['pos'] ?? -1) == $itemPos) {
                if (($item['data'] ?? '') === $newData) {
                    return [
                        'success' => true,
                        'message' => 'Storehouse item data updated successfully',
                        'item_id' => $itemId,
                        'item_pos' => $itemPos,
                    ];
                }
                break;
            }
        }

        return ['success' => false, 'message' => 'Update failed - character might be online'];
    }
}
