<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Char\CharBase;
use App\Models\Char\CharCashItem_OutputBox;
use App\Models\Char\CharItem;
use App\Models\Char\CharFellow;
use App\Models\Char\CharQuest;
use App\Models\Char\CharMail;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use App\Models\Char\CharQuest_History;

class CharController extends Controller
{
    /**
     * Получение всех персонажей из базы данных с пагинацией
     * (по умолчанию - только активные персонажи, если в запросе with_deleted=true - в том числе удаленные).
     * 
     * @return \Illuminate\Http\JsonResponse
     */
    public function GetAllCharacters(Request $request)
    {
        // 1. Валидация и параметры запроса
        $validated = $request->validate([
            'per_page' => 'sometimes|integer|min:1|max:100',
            'search' => 'sometimes|string|nullable|max:50',
            'with_deleted' => 'sometimes'
        ]);

        // Флаг: включать ли удаленных персонажей
        // Используем $request->boolean(), который корректно приводит строки ('true', '1', 'on', 'yes') к true
        $withDeleted = $request->boolean('with_deleted');

        $perPage = $validated['per_page'] ?? 15;
        $search = trim($validated['search'] ?? '');

        $sortField = $request->input('sort_field', 'DBKey');
        $sortOrder = strtolower($request->input('sort_order', 'asc')) === 'desc' ? 'desc' : 'asc';

        // whitelist для полей сортировки и выборки
        $allowedSortFields = ['DBKey', 'Name', 'Level', 'AccountName', 'Deleted'];
        if (!in_array($sortField, $allowedSortFields, true)) {
            $sortField = 'DBKey';
        }

        // 2. Запрос с выборкой только нужных полей
        $query = CharBase::query()->select($allowedSortFields);

        // Если не запрошено with_deleted=true — возвращаем только активных персонажей (Deleted = 0)
        if (!$withDeleted) {
            $query->where('Deleted', 0);
        }

        if ($search !== '') {
            $query->where(function ($q) use ($search) {
                $q->where('Name', 'like', "%{$search}%")
                    ->orWhere('AccountName', 'like', "%{$search}%");

                if (ctype_digit($search)) {
                    $q->orWhere('DBKey', (int) $search);
                }
            });
        }

        // 3. Сортировка и пагинация
        $charactersPaginator = $query->orderBy($sortField, $sortOrder)->paginate($perPage);

        // 4. Ответ
        // Если по запросу не найдено ни одного персонажа, возвращаем специальный ответ
        // для сохранения обратной совместимости API.
        if ($charactersPaginator->total() === 0) {
            return response()->json([
                'characters' => (object) [],
                'code' => -2,
                'msg' => 'Characters not found.'
            ], 200);
        }

        // Laravel автоматически преобразует пагинатор в корректный JSON,
        // включая только выбранные через select() поля.
        return response()->json([
            'code' => 0,
            'msg' => 'Characters successfully received.',
            'characters' => $charactersPaginator,
        ], 200);
    }


    /**
     * Получение всех персонажей пользователя по имени пользователя.     * 
     * (по умолчанию - только активные персонажи, если в запросе with_deleted=true - в том числе удаленные персонажи)
     *
     * @param  string $username
     * @return \Illuminate\Http\JsonResponse
     */
    public function GetUserCharacters($username)
    {
        // Флаг: включать ли удаленных персонажей (with_deleted=true/1/on/yes)
        $withDeleted = request()->boolean('with_deleted');

        // Получаем всех персонажей по $username
        $characters = CharBase::where('AccountName', $username)
            ->when(!$withDeleted, function ($q) {
                // Если не запрошены удаленные персонажи — фильтруем только активных
                $q->where('Deleted', 0);
            })
            ->get();

        // Проверяем, найдены ли персонажи
        if ($characters->isEmpty()) {
            return response()->json(['characters' => [], 'code' => -2, 'msg' => 'Characters not found for this user.'], 200);
        }

        // Подсчитываем общее время в игре для всех персонажей пользователя
        $totalPlayTime = $characters->sum('TotalPlayTime');

        // Возвращаем только DBKey, Name, Level, GuildDBKey, TotalPlayTime, Deleted для всех персонажей
        $characters = $characters->map(function ($character) {
            return [
                'DBKey' => $character->DBKey,
                'Name' => $character->Name,
                'Level' => $character->Level,
                'GuildDBKey' => $character->GuildDBKey,
                'CharPlayTime' => $character->TotalPlayTime,
                'Deleted' => $character->Deleted,
            ];
        });

        return response()->json([
            'code' => 0,
            'msg' => 'Characters successfully received.',
            'characters' => $characters,
            'totalPlayTime' => $totalPlayTime
        ], 200);
    }

    /**
     * Отправка предмета на персонажа.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function SendItemToCharacter(Request $request)
    {
        $character = CharBase::where('DBKey', $request->Owner)->first();

        if (!$character) {
            return response()->json(['code' => -1, 'msg' => 'Character not found.'], 404);
        }

        $validator = \Validator::make($request->all(), [
            'Owner' => 'required',
            'Kind' => 'required',
            'RecId' => 'required',
            'Amount' => 'required|numeric',
            'Period' => 'required|numeric',
            'evPType' => 'required',
            'Comment' => 'required'
        ], [], [
            'Owner' => '',
            'Kind' => '',
            'RecId' => '',
            'Amount' => '',
            'Period' => '',
            'evPType' => '',
            'Comment' => ''
        ]);

        if (!$validator->passes())
            return response()->json(['code' => -1, 'msg' => $validator->errors()], 400);

        CharCashItem_OutputBox::SENDITEMOUTPUTBOX($request);

        return response()->json([
            'code' => 0,
            'msg' => 'Items successfully sent.'
        ], 200);
    }


    /**
     * Получение всех данных персонажа из указанной таблицы.
     *
     * @param int $char_id
     * @param string $table_name
     * @return \Illuminate\Http\JsonResponse
     */
    public function GetCharacterData($char_id, $table_name)
    {
        // Получаем конфигурацию таблицы через приватный метод
        $config = $this->resolveTable($table_name);
        if (!$config) {
            return response()->json(['code' => -1, 'msg' => 'Table not allowed or does not exist.'], 400);
        }

        $modelClass = $config['model'];
        $key = $config['key'];

        // Проверяем существование персонажа
        if (!CharBase::find($char_id)) {
            return response()->json(['code' => -2, 'msg' => 'Character not found.'], 404);
        }

        // Получаем данные
        $data = $modelClass::where($key, $char_id)->get();

        if ($data->isEmpty()) {
            return response()->json(['code' => -3, 'msg' => 'No data found for this character in the specified table.'], 200);
        }

        return response()->json([
            'code' => 0,
            'msg' => 'Data successfully received.',
            'data' => $data
        ], 200);
    }


    /**
     * Обновление данных персонажа в указанной таблице.
     *
     * @param Request $request
     * @param int $char_id
     * @param string $table_name
     * @return \Illuminate\Http\JsonResponse
     */
    public function UpdateCharacterData(Request $request, $char_id, $table_name)
    {
        // 1. Получаем конфигурацию таблицы через приватный метод
        $config = $this->resolveTable($table_name);
        if (!$config) {
            return response()->json(['code' => -1, 'msg' => 'Table not allowed or does not exist.'], 400);
        }

        $modelClass = $config['model'];
        $ownerKey = $config['key']; // Ключ, по которому связывается с CharBase - id персонажа
        $primaryKey = $config['pk']; // Для CharBase - DBKey

        // 2. Определяем, какую запись обновляем
        if ($table_name === 'CharBase') {
            $model = $modelClass::find($char_id);
        } else {
            $recordId = $request->input($primaryKey);
            if (!$recordId) {
                return response()->json(['code' => -4, 'msg' => "Primary key '$primaryKey' is required for this table."], 400);
            }
            $model = $modelClass::find($recordId);
        }

        if (!$model) {
            return response()->json(['code' => -2, 'msg' => 'Record not found.'], 404);
        }

        // Проверяем принадлежность записи персонажу
        if ($model->{$ownerKey} != $char_id) {
            return response()->json(['code' => -5, 'msg' => 'Record does not belong to the character.'], 403);
        }

        // 3. Подготавливаем данные к обновлению – защищаем PK и владение
        $data = $request->except([$primaryKey, $ownerKey, 'DBKey', 'Owner', 'Account']);

        // 4. Обновляем модель и возвращаем ответ
        return $this->_updateAndRespond($model, $data);
    }

    /**
     * Получает конкретный предмет по $char_item_id из указанной таблицы $table предметов.
     *
     * @param string $table
     * @param int $char_item_id
     * @return \Illuminate\Http\JsonResponse
     */
    public function GetCharItem(string $table, int $char_item_id)
    {
        // Получаем конфигурацию таблицы (модель и первичный ключ)
        $config = $this->resolveTable($table);
        if (!$config) {
            return response()->json(['code' => -1, 'msg' => 'Table not allowed or does not exist.'], 400);
        }

        $modelClass = $config['model'];
        $pk = $config['pk'];

        // Ищем предмет по первичному ключу из конфигурации
        $item = $modelClass::where($pk, $char_item_id)->first();

        if (!$item) {
            return response()->json([
                'code' => -2,
                'msg' => 'Item not found.',
            ], 404);
        }

        return response()->json([
            'code' => 0,
            'msg' => 'Item successfully received.',
            'data' => $item,
        ], 200);
    }

    /**
     * Обновляет конкретный предмет по $char_item_id в указанной таблице $table предметов.
     *
     * @param \Illuminate\Http\Request $request
     * @param string $table
     * @param int $char_item_id
     * @return \Illuminate\Http\JsonResponse
     */
    public function UpdateCharItem(Request $request, string $table, int $char_item_id)
    {
        // Получаем конфигурацию таблицы (модель и первичный ключ)
        $config = $this->resolveTable($table);
        if (!$config) {
            return response()->json(['code' => -1, 'msg' => 'Table not allowed or does not exist.'], 400);
        }

        $modelClass = $config['model'];
        $pk = $config['pk'];

        // Ищем предмет по первичному ключу из конфигурации
        $item = $modelClass::where($pk, $char_item_id)->first();

        if (!$item) {
            return response()->json([
                'code' => -2,
                'msg' => 'Item not found.',
            ], 404);
        }

        // Определяем защищённые поля динамически, чтобы не дать изменить PK и владельца
        $protected = [$pk, 'Owner', 'Account'];
        $data = $request->except($protected);

        return $this->_updateAndRespond($item, $data, 'Item successfully updated.');
    }

    /**
     * Получение квеста персонажа по char_id и RecId.
     * Поддерживается выбор таблицы (CharQuest или CharQuest_History) через опциональный параметр $table.
     *
     * @param string $char_id
     * @param string $rec_id
     * @param string|null $table
     * @return \Illuminate\Http\JsonResponse
     */

    public function GetCharQuest(string $char_id, string $rec_id, ?string $table = null)
    {
        // По умолчанию используем CharQuest
        $table = $table ?? 'CharQuest';

        // Проверяем допустимость таблицы для данной операции
        $allowedQuestTables = ['CharQuest', 'CharQuest_History'];
        if (!in_array($table, $allowedQuestTables, true)) {
            return response()->json(['code' => -1, 'msg' => 'Table not allowed for this operation.'], 400);
        }

        // Проверяем существование персонажа
        if (!CharBase::find($char_id)) {
            return response()->json(['code' => -2, 'msg' => 'Character not found.'], 404);
        }

        // Получаем конфиг модели по таблице и делаем выборку по Owner и RecId
        $config = $this->resolveTable($table);
        $modelClass = $config['model'];

        $quest = $modelClass::where('Owner', $char_id)
            ->where('RecId', $rec_id)
            ->first();

        if (!$quest) {
            return response()->json([
                'code' => -3,
                'msg' => 'Quest not found for this character.',
            ], 404);
        }

        return response()->json([
            'code' => 0,
            'msg' => 'Quest successfully received.',
            'data' => $quest,
        ], 200);
    }

    /**
     * Обновление квеста персонажа по char_id и RecId.
     * Поддерживается выбор таблицы (CharQuest или CharQuest_History) через опциональный параметр $table.
     *
     * @param \Illuminate\Http\Request $request
     * @param string $char_id
     * @param string $rec_id
     * @param string|null $table
     * @return \Illuminate\Http\JsonResponse
     */

    public function UpdateCharQuest(Request $request, string $char_id, string $rec_id, ?string $table = null)
    {
        // По умолчанию используем CharQuest
        $table = $table ?? 'CharQuest';

        // Проверяем допустимость таблицы для данной операции
        $allowedQuestTables = ['CharQuest', 'CharQuest_History'];
        if (!in_array($table, $allowedQuestTables, true)) {
            return response()->json(['code' => -1, 'msg' => 'Table not allowed for this operation.'], 400);
        }

        // Проверяем существование персонажа
        if (!CharBase::find($char_id)) {
            return response()->json(['code' => -2, 'msg' => 'Character not found.'], 404);
        }

        // Получаем конфиг модели по таблице и делаем выборку по Owner и RecId
        $config = $this->resolveTable($table);
        $modelClass = $config['model'];

        $quest = $modelClass::where('Owner', $char_id)
            ->where('RecId', $rec_id)
            ->first();

        if (!$quest) {
            return response()->json([
                'code' => -3,
                'msg' => 'Quest not found for this character.',
            ], 404);
        }

        // Защищаем поля Owner и RecId от изменения
        $protected = ['Owner', 'RecId'];
        $data = $request->except($protected);

        return $this->_updateAndRespond($quest, $data, 'Quest successfully updated.');
    }

    /**
     * Получение письма персонажа по char_id и MailDBKey.
     * Возвращает ту же структуру записи, что и GetCharacterData/CharMail.
     *
     * @param string $char_id
     * @param string $mail_id
     * @return \Illuminate\Http\JsonResponse
     */
    public function GetCharMail(string $char_id, string $mail_id)
    {
        // Проверяем существование персонажа
        if (!CharBase::find($char_id)) {
            return response()->json(['code' => -2, 'msg' => 'Character not found.'], 404);
        }

        // Получаем письмо, принадлежащее персонажу
        $mail = CharMail::where('RecverDBKey', $char_id)
            ->where('MailDBKey', $mail_id)
            ->first();

        if (!$mail) {
            return response()->json([
                'code' => -3,
                'msg' => 'Mail not found for this character.',
            ], 404);
        }

        return response()->json([
            'code' => 0,
            'msg' => 'Mail successfully received.',
            'data' => $mail,
        ], 200);
    }

    /**
     * Обновление письма персонажа по char_id и MailDBKey.
     * Принимает те же поля, что и UpdateCharacterData/CharMail, защищая ключи владения и PK.
     *
     * @param \Illuminate\Http\Request $request
     * @param string $char_id
     * @param string $mail_id
     * @return \Illuminate\Http\JsonResponse
     */
    public function UpdateCharMail(Request $request, string $char_id, string $mail_id)
    {
        // Проверяем существование персонажа
        if (!CharBase::find($char_id)) {
            return response()->json(['code' => -2, 'msg' => 'Character not found.'], 404);
        }

        // Получаем письмо, принадлежащее персонажу
        $mail = CharMail::where('RecverDBKey', $char_id)
            ->where('MailDBKey', $mail_id)
            ->first();

        if (!$mail) {
            return response()->json([
                'code' => -3,
                'msg' => 'Mail not found for this character.',
            ], 404);
        }

        // Защищаем поля связи и первичный ключ
        $protected = ['RecverDBKey', 'MailDBKey', 'Owner', 'Account'];
        $data = $request->except($protected);

        return $this->_updateAndRespond($mail, $data, 'Mail successfully updated.');
    }

    //------------------------------ ПРИВАТНЫЕ МЕТОДЫ -----------------------------------------
    /**
     * Возвращает конфигурацию таблицы или null, если таблица не разрешена.
     *
     * @param string $table
     * @return array|null
     */
    private function resolveTable(string $table): ?array
    {
        return self::TABLES[$table] ?? null;
    }


    /**
     * Белый список доступных для обновления таблиц.
     * Используется в resolveTable(), GetCharacterData(), UpdateCharacterData().
     */
    private const TABLES = [
        'CharBase' => ['key' => 'DBKey', 'model' => CharBase::class, 'pk' => 'DBKey'],
        'CharItem' => ['key' => 'Owner', 'model' => CharItem::class, 'pk' => 'CharItemID'],
        'CharFellow' => ['key' => 'Owner', 'model' => CharFellow::class, 'pk' => 'DBKey'],
        'CharCashItem_OutputBox' => ['key' => 'Owner', 'model' => CharCashItem_OutputBox::class, 'pk' => 'ItemDBIndex'],
        'CharQuest' => ['key' => 'Owner', 'model' => CharQuest::class, 'pk' => 'RecId'],
        'CharQuest_History' => ['key' => 'Owner', 'model' => CharQuest_History::class, 'pk' => 'RecId'],
        'CharMail' => ['key' => 'RecverDBKey', 'model' => CharMail::class, 'pk' => 'MailDBKey'],
    ];


    /**
     * Общий метод для обновления модели и формирования ответа.
     *
     * @param \Illuminate\Database\Eloquent\Model $model
     * @param array $data
     * @param string $successMsg
     * @return \Illuminate\Http\JsonResponse
     */
    private function _updateAndRespond(\Illuminate\Database\Eloquent\Model $model, array $data, string $successMsg = 'Data successfully updated.')
    {
        // 1. Фильтруем поля для массового присвоения
        $fillable = $model->getFillable();
        $guarded = $model->getGuarded();

        if (!empty($fillable)) {
            // Если есть fillable - используем только их
            $filteredData = array_intersect_key($data, array_flip($fillable));
        } elseif ($guarded === ['*']) {
            // Если guarded = ['*'] - запрещено массовое присвоение
            return response()->json(['code' => -6, 'msg' => 'Mass assignment is not allowed for this model.'], 400);
        } else {
            // Если есть guarded (но не ['*']) - исключаем только guarded поля
            $filteredData = array_diff_key($data, array_flip($guarded));
        }

        // 2. Конвертируем null-поля в '', чтобы вместо NULL в БД сохранялась пустая строка
        $filteredData = array_map(static fn($v) => $v === null ? '' : $v, $filteredData);

        if (empty($filteredData)) {
            return response()->json(['code' => -6, 'msg' => 'No valid fields to update.'], 400);
        }

        // 3. Пытаемся обновить запись
        try {
            $ok = $model->update($filteredData);
        } catch (\Throwable $e) {
            Log::error('Database error while updating record:', ['error' => $e->getMessage()]);
            return response()->json([
                'code' => -7,
                'msg' => 'Database error while updating record.',
                'error' => $e->getMessage(),
            ], 500);
        }

        if (!$ok) {
            return response()->json([
                'code' => -8,
                'msg' => 'Update failed, record not modified.',
            ], 500);
        }

        // 4. Возвращаем успешный ответ
        return response()->json([
            'code' => 0,
            'msg' => $successMsg,
            'data' => $model->fresh()
        ], 200);
    }
	
	public function SendItemViaMail(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'ReceiverID' => 'required|integer|exists:Char.dbo.Table_CharBase,DBKey',
			'StorageID'   => 'sometimes|integer|exists:Char.dbo.Table_CharBase,DBKey',
            'RecId'      => 'required|string|max:20',
            'Amount'     => 'required|integer|min:1|max:9999',
            'Title'      => 'sometimes|string|nullable|max:33',
            'Letter'     => 'sometimes|string|nullable|max:512',
            'Money'      => 'sometimes|integer|min:0',
        ]);

        if ($validator->fails()) {
            return response()->json(['code' => -1, 'msg' => $validator->errors()], 400);
        }

        try {
            return DB::connection('Char')->transaction(function () use ($request) {
                
                $receiverId = $request->input('ReceiverID');
				$storageId = $request->input('StorageID', $receiverId);

                $receiver = CharBase::where('DBKey', $receiverId)->lockForUpdate()->first();

                if (!$receiver) {
                    return response()->json(['code' => -2, 'msg' => 'Character not found.'], 404);
                }

                $recycledMail = CharMail::where('RecverDBKey', $receiver->DBKey)
                                        ->where('Deleted', 1)
                                        ->orderBy('Slot')
                                        ->lockForUpdate()
                                        ->first();

                $isRecycled = false;
                if ($recycledMail) {
                    $nextMailSlot = $recycledMail->Slot;
                    $mailDBKey = $recycledMail->MailDBKey;
                    $isRecycled = true;
                } else {
                    $maxMailSlot = CharMail::where('RecverDBKey', $receiver->DBKey)
                                           ->lockForUpdate()
                                           ->max('Slot');
                    $nextMailSlot = ($maxMailSlot !== null) ? $maxMailSlot + 1 : 0;
                    $mailDBKey = null;
                }

                $itemCount = 1;
                $itemSlots = [-1, -1, -1, -1, -1];
                $itemsData = [];

                $maxMailItemSlot = CharItem::where('Owner', $receiver->DBKey)
                                           ->where('StrRecordKind', 'ml')
                                           ->lockForUpdate()
                                           ->max('Slot');
                $nextItemSlot = ($maxMailItemSlot !== null) ? $maxMailItemSlot + 1 : 0;

				$storage = CharBase::where('DBKey', $storageId)->lockForUpdate()->first();
				
				if (!$storage) {
					return response()->json(['code' => -2, 'msg' => 'Storage character not found.'], 404);
				}

                for ($i = 0; $i < $itemCount; $i++) {
                    $storage->ItemSerialOrder += 1;
                    
                    $itemSerial = ($storage->ItemSerialOrder * 4294967296) + $storage->DBKey;

                    $itemSlots[$i] = $nextItemSlot;

                    $itemsData[] = array_merge([
                        "Owner" => $receiver->DBKey,
                        "Slot" => $nextItemSlot,
                        "ItemSerial" => $itemSerial,
                        "KeyValueA" => $itemSerial,
                        "StrRecordKind" => "ml",
                        "Account" => 0,
                        "RecId" => $request->input('RecId'),
                        "Amount" => $request->input('Amount'),
                        "ClientSlot" => -1,
                        "Storage" => "in",
                        "ItemGrade" => "normal",
                        "Durability" => 100,
                        "MailDbKey" => null,
                        "Deleted" => 0,
                    ], $request->input('item_overrides', []));

                    $nextItemSlot++;
                }
                
                $storage->save();

                $titleText = $request->input('Title', 'Системное сообщение');
                $letterText = $request->input('Letter', 'Вам прислан подарок.');
                $money = $request->input('Money', 0);

                if ($isRecycled) {
                    $recycledMail->fill([
                        'Deleted' => 0,
                        'bRead' => 0,
                        'MailType' => 'es',
                        'SentMailDBKey' => 0,
                        'SenderDBKeyAccount' => -2,
                        'SenderDBKey' => -2,
                        'SendName' => 'GM',
                        'RecvName' => $receiver->Name,
                        'Title' => $titleText,
                        'Letter' => $letterText,
                        'binTitle' => $titleText,
                        'binLetter' => $letterText,
                        'CreateDate' => now(),
                        'ReadDate' => now(),
                        'Money' => $money,
                        'AlreadyMoney' => 0,
                        'ItemCount' => $itemCount,
                        'ItemSlot0' => -1,
                        'ItemSlot1' => -1,
                        'ItemSlot2' => -1,
                        'ItemSlot3' => -1,
                        'ItemSlot4' => -1,
                        'AlreadyItem0' => 0,
                        'AlreadyItem1' => 0,
                        'AlreadyItem2' => 0,
                        'AlreadyItem3' => 0,
                        'AlreadyItem4' => 0,
                        'Confirm' => 1,
                    ]);
                    $recycledMail->save();

                    CharItem::where('Owner', $receiver->DBKey)
                            ->where('MailDbKey', $mailDBKey)
                            ->where('StrRecordKind', 'ml')
                            ->update([
                                'Deleted' => 1,
                                'RecId' => '*',
                                'ItemSerial' => 0,
                            ]);

                } else {
                    $mailData = [
                        "RecverDBKey" => $receiver->DBKey,
                        "RecvName" => $receiver->Name,
                        "Slot" => $nextMailSlot,
                        "MailType" => "es",
                        "SentMailDBKey" => 0,
                        "SenderDBKeyAccount" => -2,
                        "SenderDBKey" => -2,
                        "SendName" => "GM",
                        "ItemCount" => $itemCount,
                        "ItemSlot0" => -1,
                        "ItemSlot1" => -1,
                        "ItemSlot2" => -1,
                        "ItemSlot3" => -1,
                        "ItemSlot4" => -1,
                        "AlreadyItem0" => 0,
                        "AlreadyItem1" => 0,
                        "AlreadyItem2" => 0,
                        "AlreadyItem3" => 0,
                        "AlreadyItem4" => 0,
                        "Confirm" => 1,
                        "Title" => $titleText,
                        "Letter" => $letterText,
                        "binTitle" => $titleText,
                        "binLetter" => $letterText,
                        "Money" => $money,
                        "AlreadyMoney" => 0,
                        "CreateDate" => now(),
                        "ReadDate" => now(),
                        "Deleted" => 0,
                    ];

                    $mail = CharMail::create($mailData);
                    $mailDBKey = $mail->MailDBKey;
                }

                foreach ($itemsData as $itemData) {
                    $itemData['MailDbKey'] = $mailDBKey;
                    CharItem::create($itemData);
                }

                if ($itemCount >= 1) {
                    $mailUpdateData = [];
                    for ($i = 0; $i < 5; $i++) {
                        $mailUpdateData["ItemSlot{$i}"] = $itemSlots[$i] ?? -1;
                    }
                    
                    CharMail::where('MailDBKey', $mailDBKey)->update($mailUpdateData);
                }

                return response()->json([
                    'code' => 0,
                    'msg' => 'Item sent successfully.',
                    'data' => [
                        'MailDBKey' => $mailDBKey,
                        'ItemSerial' => (string)$itemsData[0]['ItemSerial'],
                        'MailSlot' => $nextMailSlot,
                        'ItemSlot' => $itemSlots[0],
                        'IsRecycled' => $isRecycled,
                    ]
                ], 200);
            });
        } catch (\Throwable $e) {
            Log::error('SendItemViaMail Error: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString(),
                'ReceiverID' => $request->input('ReceiverID'),
            ]);
            return response()->json(['code' => -3, 'msg' => 'Error: ' . $e->getMessage()], 500);
        }
    }
}