<?php

namespace App\Services\PwApi\Role;

class StatusService 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 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 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 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 updateStatus($roleId, $newStatus)
    {
        $currentStatus = $this->getRoleStatus($roleId);
        if (! $currentStatus) {
            return ['success' => false, 'message' => 'Could not get role status'];
        }

        $updatedFields = [];
        foreach ($newStatus as $field => $value) {
            if (array_key_exists($field, $currentStatus)) {
                if ($currentStatus[$field] !== $value) {
                    $currentStatus[$field] = $value;
                    $updatedFields[] = $field;
                }
            }
        }

        if (empty($updatedFields)) {
            return ['success' => true, 'message' => 'No changes detected'];
        }

        $pack = pack('N*', -1, $roleId).$this->gamed->marshal($currentStatus, $this->data['role']['status']);
        $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleStatus'], $pack));

        sleep(1);
        $verifyStatus = $this->getRoleStatus($roleId);

        $verified = true;
        foreach ($updatedFields as $field) {
            if (isset($newStatus[$field]) && isset($verifyStatus[$field])) {
                $expected = is_float($newStatus[$field]) ? round($newStatus[$field], 2) : $newStatus[$field];
                $actual = is_float($verifyStatus[$field]) ? round($verifyStatus[$field], 2) : $verifyStatus[$field];
                if ($actual != $expected) {
                    $verified = false;
                    break;
                }
            }
        }

        if ($verified) {
            return [
                'success' => true,
                'message' => 'Status updated successfully',
                'updated_fields' => $updatedFields,
            ];
        }

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

    public function updateStatusField($roleId, $fieldName, $newValue)
    {
        $currentStatus = $this->getRoleStatus($roleId);
        if (! $currentStatus) {
            return ['success' => false, 'message' => 'Could not get role status'];
        }

        if (! array_key_exists($fieldName, $currentStatus)) {
            return ['success' => false, 'message' => "Unknown status field: $fieldName"];
        }

        $oldValue = $currentStatus[$fieldName];

        if ($oldValue === $newValue) {
            return ['success' => true, 'message' => 'No change detected'];
        }

        $statusDef = $this->data['role']['status'];
        $fieldType = $statusDef[$fieldName] ?? null;

        if (! $fieldType) {
            return ['success' => false, 'message' => "Field type not found for: $fieldName"];
        }

        if ($fieldType === 'octets') {
            $currentStatus[$fieldName] = $newValue;

            $pack = pack('N*', -1, $roleId).$this->gamed->marshal($currentStatus, $this->data['role']['status']);
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRoleStatus'], $pack));

            sleep(1);
            $verifyStatus = $this->getRoleStatus($roleId);

            if (isset($verifyStatus[$fieldName]) && $verifyStatus[$fieldName] === $newValue) {
                return [
                    'success' => true,
                    'message' => "$fieldName updated successfully",
                    'field' => $fieldName,
                    'old_value_length' => strlen($oldValue ?? ''),
                    'new_value_length' => strlen($newValue ?? ''),
                ];
            }

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

        } else {
            $currentStatus[$fieldName] = $newValue;

            return $this->updateStatus($roleId, [$fieldName => $newValue]);
        }
    }

    public function teleportRole($roleId, $posx = 1270.9864501953, $posy = 219.75579833984, $posz = 1034.3343505859, $worldtag = 1)
    {
        $status = $this->getRoleStatus($roleId);
        if (! $status) {
            return ['success' => false, 'message' => 'Role not found'];
        }

        $oldPosx = $status['posx'] ?? 0;
        $oldPosy = $status['posy'] ?? 0;
        $oldPosz = $status['posz'] ?? 0;
        $oldWorldtag = $status['worldtag'] ?? 1;

        $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'];
        }

        $posxPacked = strrev(pack('f', $oldPosx));
        $posyPacked = strrev(pack('f', $oldPosy));
        $poszPacked = strrev(pack('f', $oldPosz));
        $posPattern = $posxPacked.$posyPacked.$poszPacked;

        $posOffset = strpos($roleData, $posPattern);

        if ($posOffset === false) {
            for ($offset = 1000; $offset < 2500; $offset++) {
                $val1 = @unpack('f', strrev(substr($roleData, $offset, 4)))[1];
                $val2 = @unpack('f', strrev(substr($roleData, $offset + 4, 4)))[1];
                $val3 = @unpack('f', strrev(substr($roleData, $offset + 8, 4)))[1];
                $wt = @unpack('N', substr($roleData, $offset + 12, 4))[1];

                if (abs($val1 - $oldPosx) < 1 && abs($val2 - $oldPosy) < 1 &&
                    abs($val3 - $oldPosz) < 1 && $wt == $oldWorldtag) {
                    $posOffset = $offset;
                    break;
                }
            }
        }

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

        $oldPos = [
            'posx' => $oldPosx,
            'posy' => $oldPosy,
            'posz' => $oldPosz,
            'worldtag' => $oldWorldtag,
        ];

        $newData = substr($roleData, 0, $posOffset);
        $newData .= strrev(pack('f', (float) $posx));
        $newData .= strrev(pack('f', (float) $posy));
        $newData .= strrev(pack('f', (float) $posz));
        $newData .= pack('N', (int) $worldtag);
        $newData .= substr($roleData, $posOffset + 16);

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

        sleep(1);
        $verifyStatus = $this->getRoleStatus($roleId);
        $verifyPosx = $verifyStatus['posx'] ?? 0;

        if (abs($verifyPosx - $posx) < 1) {
            return [
                'success' => true,
                'message' => 'Teleport berhasil',
                'old_position' => $oldPos,
                'new_position' => ['posx' => $posx, 'posy' => $posy, 'posz' => $posz, 'worldtag' => $worldtag],
            ];
        }

        return ['success' => false, 'message' => 'Teleport gagal - karakter mungkin online'];
    }

    public function setMeridianLevelWithPutRole($roleId, $level)
    {
        // Generate meridian hex data for the specified level
        $meridianHex = $this->generateMeridianHex($level);

        // Get current role status
        $status = $this->getRoleStatus($roleId);
        if (! $status) {
            return ['success' => false, 'message' => 'Role not found'];
        }

        // Get full role data using getRole
        $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'];
        }

        // Get current meridian data to find its position
        $currentMeridianHex = $status['meridian_data'] ?? '';

        // Handle empty meridian data - need to marshal full role with new meridian
        if (empty($currentMeridianHex)) {
            // Unmarshal the full role data
            $fullRole = $this->gamed->unmarshal($roleData, $this->data['role']);
            if (! is_array($fullRole)) {
                return ['success' => false, 'message' => 'Failed to unmarshal role data'];
            }

            // Set the meridian data
            $fullRole['status']['meridian_data'] = $meridianHex;

            // Marshal back and send via putRole
            $newRoleData = $this->gamed->marshal($fullRole, $this->data['role']);
            $putPack = pack('NNC', -1, $roleId, 1).$newRoleData;
            $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

            return [
                'success' => true,
                'message' => "Meridian level set to {$level} successfully",
                'old_meridian' => '',
                'new_meridian' => $meridianHex,
                'level' => $level,
            ];
        }

        $currentMeridianBin = pack('H*', $currentMeridianHex);
        $meridianLen = strlen($currentMeridianBin);

        $meridianOffset = strpos($roleData, $currentMeridianBin);

        if ($meridianOffset === false) {
            // If can't find in raw data, try marshaling approach
            $fullRole = $this->gamed->unmarshal($roleData, $this->data['role']);
            if (is_array($fullRole)) {
                $fullRole['status']['meridian_data'] = $meridianHex;
                $newRoleData = $this->gamed->marshal($fullRole, $this->data['role']);
                $putPack = pack('NNC', -1, $roleId, 1).$newRoleData;
                $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['putRole'], $putPack));

                return [
                    'success' => true,
                    'message' => "Meridian level set to {$level} successfully",
                    'old_meridian' => $currentMeridianHex,
                    'new_meridian' => $meridianHex,
                    'level' => $level,
                ];
            }

            return ['success' => false, 'message' => 'Could not find meridian data position in raw role data'];
        }

        $newMeridianBin = pack('H*', $meridianHex);

        if (strlen($newMeridianBin) !== $meridianLen) {
            return ['success' => false, 'message' => 'Meridian data length mismatch'];
        }

        // Replace meridian data in raw role data
        $newData = substr($roleData, 0, $meridianOffset);
        $newData .= $newMeridianBin;
        $newData .= substr($roleData, $meridianOffset + $meridianLen);

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

        return [
            'success' => true,
            'message' => "Meridian level set to {$level} successfully",
            'old_meridian' => $currentMeridianHex,
            'new_meridian' => $meridianHex,
            'level' => $level,
        ];
    }

    /**
     * Generate meridian hex data for a specific level
     */
    private function generateMeridianHex($level)
    {
        $data = [
            'lvl' => $level,
            'life_dot' => 0,
            'die_dot' => 0,
            'free_up' => 5,
            'paid_up' => 100,
            'login_time' => 0,
            'login_days' => 1,
            'trigrams_map1' => 0,
            'trigrams_map2' => 0,
            'trigrams_map3' => 0,
            'reserved1' => 0,
            'reserved2' => 0,
            'reserved3' => 0,
            'reserved4' => 0,
        ];

        $hex = '';
        foreach ($data as $value) {
            $hex .= str_pad(dechex($value), 8, '0', STR_PAD_LEFT);
        }

        return $hex;
    }

    public function maxMeridian($roleId, $meridianHex = null)
    {
        if ($meridianHex === null) {
            $meridianHex = '0000005000000000000000000000000500000064000000000000000100000000000000000000000000000000000000000000000000000000';
        }

        $status = $this->getRoleStatus($roleId);
        if (! $status || empty($status['meridian_data'])) {
            return ['success' => false, 'message' => 'Role not found or no meridian data'];
        }

        $currentMeridianHex = $status['meridian_data'];
        $currentMeridianBin = pack('H*', $currentMeridianHex);
        $meridianLen = strlen($currentMeridianBin);

        $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'];
        }

        $meridianOffset = strpos($roleData, $currentMeridianBin);

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

        $newMeridianBin = pack('H*', $meridianHex);

        if (strlen($newMeridianBin) !== $meridianLen) {
            return ['success' => false, 'message' => 'Meridian data length mismatch'];
        }

        $newData = substr($roleData, 0, $meridianOffset);
        $newData .= $newMeridianBin;
        $newData .= substr($roleData, $meridianOffset + $meridianLen);

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

        sleep(1);
        $verify = $this->gamed->SendToGamedBD($this->gamed->createHeader($this->data['code']['getRole'], $pack));
        $verifyData = $this->gamed->deleteHeader($verify);
        $verifyMeridianHex = bin2hex(substr($verifyData, $meridianOffset, $meridianLen));

        if ($verifyMeridianHex === $meridianHex) {
            return [
                'success' => true,
                'message' => 'Meridian maxed successfully',
                'old_meridian' => $currentMeridianHex,
                'new_meridian' => $meridianHex,
            ];
        }

        return ['success' => false, 'message' => 'Max meridian gagal - karakter mungkin online'];
    }

    public function clearStorehousePassword($roleId)
    {
        try {
            $status = $this->getRoleStatus($roleId);
            if (! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            $oldPassword = $status['storehousepasswd'] ?? '';

            if (empty($oldPassword)) {
                return ['success' => true, 'message' => 'Storehouse password already empty'];
            }

            $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'];
            }

            $oldPasswordBin = pack('H*', $oldPassword);
            $passwordOffset = strpos($roleData, $oldPasswordBin);

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

            $passwordLen = strlen($oldPasswordBin);

            $lengthPrefixStart = $passwordOffset;
            for ($i = 1; $i <= 4; $i++) {
                $checkOffset = $passwordOffset - $i;
                if ($checkOffset < 0) {
                    break;
                }

                $tmp = 0;
                $testData = substr($roleData, $checkOffset, $i + $passwordLen);
                $decodedLen = $this->gamed->unpackCuint($testData, $tmp);

                if ($decodedLen == $passwordLen && $tmp == $i) {
                    $lengthPrefixStart = $checkOffset;
                    break;
                }
            }

            $oldOctetsLen = $passwordOffset - $lengthPrefixStart + $passwordLen;

            $newPasswordOctets = $this->gamed->cuint(0);

            $newData = substr($roleData, 0, $lengthPrefixStart);
            $newData .= $newPasswordOctets;
            $newData .= substr($roleData, $lengthPrefixStart + $oldOctetsLen);

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

            sleep(1);
            $verifyStatus = $this->getRoleStatus($roleId);
            $newPassword = $verifyStatus['storehousepasswd'] ?? '';

            if (empty($newPassword)) {
                return ['success' => true, 'message' => 'Storehouse password cleared successfully'];
            }

            return ['success' => false, 'message' => 'Clear password failed - character might be online'];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => 'Error: '.$e->getMessage()];
        }
    }

    public function resetTitle($roleId)
    {
        try {
            $status = $this->getRoleStatus($roleId);
            if (! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            $result = $this->updateStatusFields($roleId, ['title_data' => '']);

            if ($result['success']) {
                return ['success' => true, 'message' => 'Title reset successfully.'];
            }

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

    public function updateStatusFields($roleId, $fields)
    {
        try {
            $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 || ! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            foreach ($fields as $field => $value) {
                if (! array_key_exists($field, $status)) {
                    return ['success' => false, 'message' => "Field '$field' not found in status"];
                }
                $status[$field] = $value;
            }

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

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

            sleep(1);
            $verifyStatus = $this->getRoleStatus($roleId);

            $allUpdated = true;
            foreach ($fields as $field => $value) {
                if ($verifyStatus[$field] != $value) {
                    $allUpdated = false;
                    break;
                }
            }

            if ($allUpdated) {
                $fieldNames = implode(', ', array_keys($fields));

                return ['success' => true, 'message' => "Status fields updated successfully: $fieldNames"];
            }

            return ['success' => false, 'message' => 'Update failed - character might be online'];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => 'Error: '.$e->getMessage()];
        }
    }

    public function resetOctetsField($roleId, $fieldName)
    {
        try {
            $status = $this->getRoleStatus($roleId);
            if (! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            if (! array_key_exists($fieldName, $status)) {
                return ['success' => false, 'message' => "Field '$fieldName' not found in status"];
            }

            $oldData = $status[$fieldName] ?? '';

            if (empty($oldData)) {
                $fieldLabel = ucfirst(str_replace('_', ' ', $fieldName));

                return ['success' => true, 'message' => "$fieldLabel already empty"];
            }

            $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 = pack('H*', $oldData);
            $dataOffset = strpos($roleData, $oldDataBin);

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

            $dataLen = strlen($oldDataBin);

            $lengthPrefixStart = $dataOffset;
            for ($i = 1; $i <= 4; $i++) {
                $checkOffset = $dataOffset - $i;
                if ($checkOffset < 0) {
                    break;
                }

                $tmp = 0;
                $testData = substr($roleData, $checkOffset, $i + $dataLen);
                $decodedLen = $this->gamed->unpackCuint($testData, $tmp);

                if ($decodedLen == $dataLen && $tmp == $i) {
                    $lengthPrefixStart = $checkOffset;
                    break;
                }
            }

            $oldOctetsLen = $dataOffset - $lengthPrefixStart + $dataLen;

            $newOctets = $this->gamed->cuint(0);

            $newData = substr($roleData, 0, $lengthPrefixStart);
            $newData .= $newOctets;
            $newData .= substr($roleData, $lengthPrefixStart + $oldOctetsLen);

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

            sleep(1);
            $verifyStatus = $this->getRoleStatus($roleId);
            $newFieldData = $verifyStatus[$fieldName] ?? '';

            if (empty($newFieldData)) {
                $fieldLabel = ucfirst(str_replace('_', ' ', $fieldName));

                return ['success' => true, 'message' => "$fieldLabel reset successfully"];
            }

            return ['success' => false, 'message' => "Reset $fieldName failed - character might be online"];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => 'Error: '.$e->getMessage()];
        }
    }

    public function updateOctetsField($roleId, $fieldName, $newHexData)
    {
        try {
            $status = $this->getRoleStatus($roleId);
            if (! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            if (! array_key_exists($fieldName, $status)) {
                return ['success' => false, 'message' => "Field '$fieldName' not found in status"];
            }

            $oldData = $status[$fieldName] ?? '';

            if ($oldData === $newHexData) {
                return ['success' => true, 'message' => "$fieldName already has the desired value"];
            }

            $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'];
            }

            $newDataBin = pack('H*', $newHexData);
            $newDataLen = strlen($newDataBin);
            $newOctets = $this->gamed->cuint($newDataLen).$newDataBin;

            if (! empty($oldData)) {
                $oldDataBin = pack('H*', $oldData);
                $dataOffset = strpos($roleData, $oldDataBin);

                if ($dataOffset !== false) {
                    $dataLen = strlen($oldDataBin);

                    $lengthPrefixStart = $dataOffset;
                    for ($i = 1; $i <= 4; $i++) {
                        $checkOffset = $dataOffset - $i;
                        if ($checkOffset < 0) {
                            break;
                        }

                        $tmp = 0;
                        $testData = substr($roleData, $checkOffset, $i + $dataLen);
                        $decodedLen = $this->gamed->unpackCuint($testData, $tmp);

                        if ($decodedLen == $dataLen && $tmp == $i) {
                            $lengthPrefixStart = $checkOffset;
                            break;
                        }
                    }

                    $oldOctetsLen = $dataOffset - $lengthPrefixStart + $dataLen;

                    $newRoleData = substr($roleData, 0, $lengthPrefixStart);
                    $newRoleData .= $newOctets;
                    $newRoleData .= substr($roleData, $lengthPrefixStart + $oldOctetsLen);

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

                    sleep(1);
                    $verifyStatus = $this->getRoleStatus($roleId);
                    $newFieldData = $verifyStatus[$fieldName] ?? '';

                    if ($newFieldData === $newHexData) {
                        return [
                            'success' => true,
                            'message' => "$fieldName updated successfully",
                            'old_length' => strlen($oldData),
                            'new_length' => strlen($newHexData),
                        ];
                    }
                }
            }

            return $this->updateStatusField($roleId, $fieldName, $newHexData);

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

    public function resetCultivation($roleId, $skillsHex = null)
    {
        try {
            $status = $this->getRoleStatus($roleId);
            if (! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            $oldLevel2 = $status['level2'] ?? 0;

            if ($oldLevel2 == 0) {
                return ['success' => false, 'message' => 'Cultivation is not unlocked yet'];
            }

            $newLevel2 = ($oldLevel2 == 22) ? 32 : 22;

            if ($skillsHex === null || empty($skillsHex)) {
                $skillsHex = '060000009f000000000000000a000000a700000000000000010000007a050000000000000a000000a1000000000000000a0000009e000000000000000a000000a0000000000000000a000000';
            }

            $result = $this->updateStatusFields($roleId, ['level2' => $newLevel2]);

            if (! $result['success']) {
                return $result;
            }

            $skillsResult = $this->updateOctetsField($roleId, 'skills', $skillsHex);

            if (! $skillsResult['success']) {
                return [
                    'success' => true,
                    'message' => 'Cultivation changed successfully, but skills reset failed: '.($skillsResult['message'] ?? 'Unknown error'),
                    'old_level2' => $oldLevel2,
                    'new_level2' => $newLevel2,
                ];
            }

            return [
                'success' => true,
                'message' => 'Cultivation changed and skills reset successfully',
                'old_level2' => $oldLevel2,
                'new_level2' => $newLevel2,
            ];
        } catch (\Exception $e) {
            return ['success' => false, 'message' => 'Error: '.$e->getMessage()];
        }
    }

    public function resetSkills($roleId, $skillsHex = null)
    {
        try {
            if ($skillsHex === null) {
                $skillsHex = '060000009f000000000000000a000000a700000000000000010000007a050000000000000a000000a1000000000000000a0000009e000000000000000a000000a0000000000000000a000000';
            }

            $status = $this->getRoleStatus($roleId);
            if (! $status) {
                return ['success' => false, 'message' => 'Role not found'];
            }

            $oldSkills = $status['skills'] ?? '';

            $newSkillsBin = pack('H*', $skillsHex);
            $oldSkillsBin = ! empty($oldSkills) ? pack('H*', $oldSkills) : '';

            if ($oldSkillsBin === $newSkillsBin) {
                return ['success' => true, 'message' => 'Skills are already reset'];
            }

            $result = $this->updateStatusField($roleId, 'skills', $skillsHex);

            if ($result['success']) {
                return [
                    'success' => true,
                    'message' => 'Skills reset successfully',
                    'old_skills_length' => strlen($oldSkills),
                    'new_skills_length' => strlen($skillsHex),
                ];
            }

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