<?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;

class CharController extends Controller
{
    /**
     * Получение всех персонажей из базы данных с пагинацией.
     *     * 
     * @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',
        ]);

        $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'];
        if (!in_array($sortField, $allowedSortFields, true)) {
            $sortField = 'DBKey';
        }

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

        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);
    }

    
    /**
     * Получение всех персонажей пользователя по AccountDBID.
     *
     * @param  string $username
     * @return \Illuminate\Http\JsonResponse
     */
    public function GetUserCharacters($username)
    {
        // Получаем всех персонажей по $username
        $characters = CharBase::where('AccountName', $username)->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 персонажей
        $characters = $characters->map(function ($character) {
            return [
                'DBKey' => $character->DBKey,
                'Name' => $character->Name,
                'Level' => $character->Level,
                'GuildDBKey' => $character->GuildDBKey,
                'CharPlayTime' => $character->TotalPlayTime
            ];
        });

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

    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'];
        $primaryKey  = $config['pk'];

        // 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. Фильтруем поля для массового присвоения
        $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));
        }

        // Конвертируем 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);
        }

        // 5. Пытаемся обновить запись; если что-то пошло не так — отправляем ошибку
        try {
            $ok = $model->update($filteredData);
        } catch (\Throwable $e) {
            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);
        }

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


    /**
     * Возвращает конфигурацию таблицы или 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' => 'FellowID'],
    ];
}