<?php

namespace App\Services\PwApi\Money;

class MoneyService extends \App\Services\PwApi\Core\PwApiService
{
    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 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 getUser($id)
    {
        $pack = pack('N*', -1, $id, 1, 1);
        $data = $this->gamed->cuint($this->data['code']['getUser']).$this->gamed->cuint(strlen($pack)).$pack;
        $send = $this->gamed->SendToGamedBD($data);
        $strlarge = unpack('H', substr($send, 2, 1));
        if (substr($strlarge[1], 0, 1) == 8) {
            $tmp = 12;
        } else {
            $tmp = 11;
        }
        $send = substr($send, $tmp);
        $user = $this->gamed->unmarshal($send, $this->data['user']['info']);
        $user['login_ip'] = $this->gamed->getIp($this->gamed->reverseOctet(substr($user['login_record'], 8, 8)));
        $user['login_time'] = $this->gamed->getTime(substr($user['login_record'], 0, 8));

        return $user;
    }

    public function updatePocketMoney($roleId, $newMoney)
    {
        $pocket = $this->getRoleInventory($roleId);
        if (! $pocket) {
            return ['success' => false, 'message' => 'Could not get pocket data'];
        }

        $oldMoney = $pocket['money'] ?? 0;

        if ($oldMoney == $newMoney) {
            return ['success' => true, 'message' => 'Money unchanged', 'money' => $newMoney];
        }

        $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) < 100) {
            return ['success' => false, 'message' => 'Failed to get role data'];
        }

        $icapacity = $pocket['icapacity'] ?? 32;
        $timestamp = $pocket['timestamp'] ?? 0;
        $pattern = pack('NNN', $icapacity, $timestamp, $oldMoney);
        $offset = strpos($roleData, $pattern);

        if ($offset !== false) {
            $moneyOffset = $offset + 8;
            $moneyBin = pack('N', $newMoney);
            $roleData = substr_replace($roleData, $moneyBin, $moneyOffset, 4);
        } else {
            $pattern = pack('NN', $icapacity, $timestamp);
            $offset = strpos($roleData, $pattern);
            if ($offset !== false) {
                $moneyOffset = $offset + 8;
                $moneyBin = pack('N', $newMoney);
                $roleData = substr_replace($roleData, $moneyBin, $moneyOffset, 4);
            }
        }

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

        return [
            'success' => true,
            'message' => 'Pocket money updated',
            'old_money' => $oldMoney,
            'new_money' => $newMoney,
        ];
    }

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

        $oldMoney = $storehouse['money'] ?? 0;

        if ($oldMoney == $newMoney) {
            return ['success' => true, 'message' => 'Money unchanged', 'money' => $newMoney];
        }

        $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) < 100) {
            return ['success' => false, 'message' => 'Failed to get role data'];
        }

        $capacity = $storehouse['capacity'] ?? 16;
        $pattern = pack('NN', $capacity, $oldMoney);
        $offset = strpos($roleData, $pattern);

        if ($offset !== false) {
            $moneyOffset = $offset + 4;
            $moneyBin = pack('N', $newMoney);
            $roleData = substr_replace($roleData, $moneyBin, $moneyOffset, 4);
        }

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

        return [
            'success' => true,
            'message' => 'Storehouse money updated',
            'old_money' => $oldMoney,
            'new_money' => $newMoney,
        ];
    }

    public function updateMoney($roleId, $pocketMoney = null, $storehouseMoney = null)
    {
        if ($pocketMoney === null && $storehouseMoney === null) {
            return ['success' => false, 'message' => 'No money value specified'];
        }

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

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

        $oldPocketMoney = $pocket['money'] ?? 0;
        $oldStoreMoney = $storehouse['money'] ?? 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'];
        }

        $newData = $roleData;
        $updated = [];

        if ($pocketMoney !== null) {
            $pocketIcapacity = $pocket['icapacity'] ?? 32;
            $pocketTimestamp = $pocket['timestamp'] ?? 0;

            $pocketPattern = pack('NNN', $pocketIcapacity, $pocketTimestamp, $oldPocketMoney);
            $pocketOffset = strpos($roleData, $pocketPattern);

            if ($pocketOffset === false) {
                $pocketPattern = pack('NN', $pocketIcapacity, $pocketTimestamp);
                $pocketOffset = strpos($roleData, $pocketPattern);
            }

            if ($pocketOffset !== false) {
                $pocketMoneyOffset = $pocketOffset + 8;
                $newData = substr($newData, 0, $pocketMoneyOffset)
                         .pack('N', $pocketMoney)
                         .substr($newData, $pocketMoneyOffset + 4);
                $updated['pocket'] = $pocketMoney;
            }
        }

        if ($storehouseMoney !== null && $storehouse) {
            $storeCapacity = $storehouse['capacity'] ?? 16;
            $storeItemCount = count($storehouse['store'] ?? []);

            $storePattern = pack('NN', $storeCapacity, $oldStoreMoney);
            $storeOffset = null;
            $searchOffset = 0;

            while (($pos = strpos($newData, $storePattern, $searchOffset)) !== false) {
                $countByte = ord($newData[$pos + 8]);
                if ($countByte == $storeItemCount) {
                    $storeOffset = $pos;
                    break;
                }
                $searchOffset = $pos + 1;
            }

            if ($storeOffset !== null) {
                $storeMoneyOffset = $storeOffset + 4;
                $newData = substr($newData, 0, $storeMoneyOffset)
                         .pack('N', $storehouseMoney)
                         .substr($newData, $storeMoneyOffset + 4);
                $updated['storehouse'] = $storehouseMoney;
            }
        }

        if (empty($updated)) {
            return ['success' => false, 'message' => 'Could not find money fields in raw data'];
        }

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

        sleep(1);
        $verifyPocket = $this->getRoleInventory($roleId);
        $verifyStore = $this->getRoleStorehouse($roleId);

        $success = true;
        if ($pocketMoney !== null && ($verifyPocket['money'] ?? 0) != $pocketMoney) {
            $success = false;
        }
        if ($storehouseMoney !== null && ($verifyStore['money'] ?? 0) != $storehouseMoney) {
            $success = false;
        }

        if ($success) {
            return [
                'success' => true,
                'message' => 'Money updated successfully',
                'updated' => $updated,
                'pocket_money' => $verifyPocket['money'] ?? 0,
                'storehouse_money' => $verifyStore['money'] ?? 0,
            ];
        }

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

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

        $oldMoney = $storehouse['money'] ?? 0;

        if ($oldMoney == $newMoney) {
            return ['success' => true, 'message' => 'Money unchanged', 'money' => $newMoney];
        }

        $storehouse['money'] = $newMoney;

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

        sleep(1);
        $verifyStore = $this->getRoleStorehouse($roleId);
        $verifyMoney = $verifyStore['money'] ?? 0;

        if ($verifyMoney == $newMoney) {
            return [
                'success' => true,
                'message' => 'Storehouse money updated successfully',
                'old_money' => $oldMoney,
                'new_money' => $newMoney,
            ];
        }

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

    public function putPocketMoney($roleId, $newMoney)
    {
        $pocket = $this->getRoleInventory($roleId);
        if (! $pocket) {
            return ['success' => false, 'message' => 'Could not get pocket data'];
        }

        $oldMoney = $pocket['money'] ?? 0;

        if ($oldMoney == $newMoney) {
            return ['success' => true, 'message' => 'Money unchanged', 'money' => $newMoney];
        }

        $pocket['money'] = $newMoney;

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

        sleep(1);
        $verifyPocket = $this->getRoleInventory($roleId);
        $verifyMoney = $verifyPocket['money'] ?? 0;

        if ($verifyMoney == $newMoney) {
            return [
                'success' => true,
                'message' => 'Pocket money updated successfully',
                'old_money' => $oldMoney,
                'new_money' => $newMoney,
            ];
        }

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

    public function getCash($userId)
    {
        $pack = pack('N*', -1, $userId);
        $pack = $this->gamed->createHeader($this->data['code']['GetCashTotal'], $pack);
        $send = $this->gamed->SendToGamedBD($pack);
        $data = $this->gamed->deleteHeader($send);
        $result = $this->gamed->unmarshal($data, $this->data['getCashTotal']);

        return $result['cash_total'] ?? 0;
    }

    public function getCurrentCash($userId)
    {
        $user = $this->getUser($userId);

        if (! $user) {
            return 0;
        }

        $cashAdd = $user['cash_add'] ?? 0;
        $cashUsed = $user['cash_used'] ?? 0;

        return ($cashAdd - $cashUsed) / 100;
    }
}
