import { timerLabels, validQR } from '../src/utils/presentation.js' import type { TimerType } from '../src/types/screen.js' import { ResponseFieldError } from './request-error.js' import type { ScreenMode, ScreenPlan } from '../src/types/screen.js' import { integer, object, text } from './remote-state.js' const modes = [ 'UNSPECIFIED', 'COVER', 'INTRO', 'INTRO_DONE', 'CONCLUSION', 'CONCLUSION_DONE', 'RESULTS', 'TIMER', 'TEXT', 'QR', 'TEAMS', ] export function parsePlan(value: unknown): ScreenPlan { const plan = value == null ? {} : object(value, 'plan') const list = plan.steps ?? [] if (!Array.isArray(list)) throw new ResponseFieldError('plan.steps', 'массив', list, 'Некорректные шаги плана') const steps = list.map((value, index) => { const field = `plan.steps[${index}]` const step = object(value, field) let mode: number try { mode = integer( typeof step.mode === 'string' && step.mode.startsWith('SCREEN_MODE_') ? modes.indexOf(step.mode.slice(12)) : step.mode, 1, 10, `${field}.mode`, ) } catch { throw new ResponseFieldError( `${field}.mode`, 'режим экрана 1–10 или enum ScreenMode', step.mode, 'Некорректный режим экрана', ) } let content = text(step.text ?? '', `${field}.text`).trim() if (mode === 3 || mode === 5) { content = mode === 3 ? 'Успехов' : 'Спасибо за игру!' mode = 8 } const timerType = parseTimerType(step.timerType) const qrUrl = text(step.qrUrl ?? '', `${field}.qrUrl`) if (mode === 9 && !validQR(qrUrl, content)) throw new Error('Укажите ссылку HTTP/HTTPS до 2048 байт и текст до 10000 байт') const label = text(step.timerLabel ?? '', `${field}.timerLabel`).trim() const seconds = integer( step.timerDurationSeconds ?? 0, 0, 86400, `${field}.timerDurationSeconds`, ) if (mode === 8 && (!content || Buffer.byteLength(content) > 10000)) throw new ResponseFieldError( `${field}.text`, 'строка от 1 до 10000 байт', step.text, 'Текст шага должен содержать от 1 до 10000 байт', ) if (mode === 7 && (!seconds || Buffer.byteLength(label) > 200)) throw new ResponseFieldError( !seconds ? `${field}.timerDurationSeconds` : `${field}.timerLabel`, !seconds ? 'число от 1 до 86400' : 'строка до 200 байт', !seconds ? step.timerDurationSeconds : step.timerLabel, 'Укажите таймер от 1 до 86400 секунд и подпись до 200 байт', ) return { mode: mode as ScreenMode, text: content, timerLabel: mode === 7 ? label || timerLabels[timerType] : label, timerDurationSeconds: seconds, ...(qrUrl ? { qrUrl } : {}), ...(timerType ? { timerType } : {}), } }) return { steps, currentStep: integer( plan.currentStep ?? 0, 0, Math.max(0, steps.length - 1), 'plan.currentStep', ), } } export function parseTimerType(value: unknown): TimerType { const names = [ 'SCREEN_TIMER_TYPE_BREAK', 'SCREEN_TIMER_TYPE_BEFORE_GAME', 'SCREEN_TIMER_TYPE_UNTIL_END', ] return integer( typeof value === 'string' && value.startsWith('SCREEN_TIMER_TYPE_') ? names.indexOf(value) : (value ?? 0), 0, 2, 'timerType', ) as TimerType }