/**
 * Интерфейсы и типы для работы с приложением.
 * Определяют основные контракты и структуры данных для взаимодействия
 */
import { AppContext } from '../AppContext';
import { BotController } from '../../controller';
import { IncomingMessage, ServerResponse } from 'node:http';
import { IButtonType, Buttons, IImageType, ISound } from '../../components';
import { IModelRes, TQueryCb, IQuery, IQueryData } from '../../models';
import { Bot } from '../Bot';
/**
 * Тип содержимого запроса к голосовому навыку или боту
 * Определяет возможные форматы данных, которые могут быть переданы навыку/боту
 *
 * @remarks
 * Возможные значения:
 * - string: JSON или текстовое содержимое запроса
 *   ```ts
 *   const content: TBotContent = '{"text": "Привет мир!"}';
 *   ```
 * - boolean: Флаг состояния запроса
 *   ```ts
 *   const content: TBotContent = true; // запрос успешно обработан
 *   ```
 * - null: Пустой запрос или ошибка
 *   ```ts
 *   const content: TBotContent = null; // запрос не содержит данных
 *   ```
 */
export type TBotContent = object | string | null;
/**
 * Тип авторизационного токена.
 * Определяет формат данных для авторизации пользователя
 *
 * @remarks
 * Возможные значения:
 * - string: Валидный токен авторизации
 *   ```ts
 *   const auth: TBotAuth = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
 *   ```
 * - null: Пользователь не авторизован или авторизация не требуется
 *   ```ts
 *   const auth: TBotAuth = null; // авторизация отсутствует
 *   ```
 */
export type TBotAuth = string | null;
/**
 * Интерфейс для базового ответ
 */
export interface IBotResponse {
    /**
     * Статус код ответа.
     *  - 200 в случае успеха
     *  - 400 в случае если отправлен пустой запрос, или фреймворк завершил обработку с ошибкой.
     *  - 500 в случае ошибки самого сервера
     */
    statusCode: number;
    /**
     * Содержимое, которое необходимо отобразить
     */
    body: string | object;
}
/**
 * Интерфейс для состояния, которое придет в пользовательском обработчике ответа
 */
export interface IBotResponseState extends IBotResponse {
    /**
     * Базовый метод для отправки ответа
     * @param res
     * @param state
     */
    defaultSend: (res: ServerResponse, state: IBotResponse) => void;
}
/**
 * Тип для пользовательской обработки запроса
 * @param reg - Объект входящего запроса (IncomingMessage или совместимый)
 * @param res - Объект ответа (ServerResponse или совместимый)
 * @param state Состояние выполненного запроса
 */
export type TBotResponseCb = (reg: IncomingMessage, res: ServerResponse, state: IBotResponseState) => void;
/**
 * Интерфейс для плагина в виде объекта.
 */
export interface IPlugin {
    /**
     * Метод инициализации плагина.
     * Вызывается один раз при подключении через `bot.use()`.
     * @param appContext Контекст приложения
     * @param bot Основной класс приложения
     */
    init: (appContext: AppContext, bot: Bot) => void;
    /**
     * Метод, который вызывается при уничтожении плагина.
     * В данном методе можно добавить отписку, либо выполнить другие действия.
     * @param bot Основной класс приложения
     */
    destroy: (bot: Bot) => void | Promise<void>;
}
/**
 * Возвращаемый результат после инициализации плагина.
 * Возвращает либо void, либо функция, которая будет вызвана после уничтожения.
 */
export type TPluginFnResult = void | ((bot: Bot) => void);
/**
 * Тип для плагина в виде функции.
 * Должен иметь свойство `isPlugin = true` для отличия от обычных функций.
 */
export interface IPluginFn {
    /**
     * Конструктор функции регистрации плагина
     * @extends
     * ```ts
     * function myPlugin(appContext: AppContext, bot: Bot) {
     *      // Какая-то ваша логика
     *      return () => {
     *          // Функция, которая будет вызвана при уничтожении.
     *      }
     * }
     * ```
     * @param appContext Контекст приложения
     * @param bot Основной класс приложения
     */
    (appContext: AppContext, bot: Bot): TPluginFnResult;
    /**
     * Флаг, говорящий о том, что функция является плагином
     */
    isPlugin: boolean;
}
/**
 * Объединённый тип для любого плагина.
 */
export type TPlugin = IPlugin | IPluginFn;
/**
 * Интерфейс для адаптеров платформы (Алиса, Салют, Telegram, VK и др.).
 *
 * Обеспечивает унификацию обработки запросов от разных платформ.
 * Реализуется как плагин (`IPlugin`) и регистрируется в приложении.
 */
export interface IPlatformAdapter<TQuery = unknown> extends IPlugin {
    /**
     * Определяет, принадлежит ли входящий запрос данной платформе.
     *
     * Метод проверяет заголовки или структуру тела запроса.
     * Используется для маршрутизации входящих запросов между адаптерами.
     *
     * @param query - входящий запрос (тело или объект)
     * @param headers - HTTP-заголовки (если есть)
     * @returns `true`, если запрос относится к этой платформе, иначе `false`
     *
     * @example
     * ```ts
     * // Telegram проверяет наличие заголовка 'X-Telegram-Bot-Api-Secret-Token'
     * isPlatformOnQuery(query, headers) {
     *   return headers?.['x-telegram-bot-api-secret-token'] === this.secret;
     * }
     * ```
     */
    isPlatformOnQuery: (query: TQuery, headers?: Record<string, unknown>) => boolean;
    /**
     * Проверяет полученный запрос от платформы на корректность.
     * Реализация зависит от адаптера, как правило, в чувствительных платформах есть токен, который приходит с запросом, и желательно проверять что пришедший токен соответствует тому, что сохранен в настройках.
     * @param query
     * @param headers
     */
    isCorrectQuery: (query: TQuery, headers?: Record<string, unknown>) => boolean;
    /**
     * Инициализирует данные запроса в контроллере приложения.
     *
     * Парсит входящий запрос и заполняет `controller.queryData`, `controller.user` и другие поля.
     * Вызывается после подтверждения, что запрос принадлежит этой платформе.
     *
     * @param query - входящий запрос
     * @param controller - контроллер приложения для текущего запроса
     * @returns `false`, если запрос повреждён или не может быть обработан; иначе `true`
     */
    setQueryData: (query: TQuery, controller: BotController) => boolean | Promise<boolean>;
    /**
     * Формирует тело ответа для отправки пользователю.
     *
     * Возвращает платформо-специфичный ответ (например, JSON для Алисы).
     * Для платформ, которые отправляют ответ напрямую (например, Telegram через `sendMessage`),
     * метод может возвращать `{ ok: true }` или аналог.
     *
     * @param controller - контроллер с готовым ответом
     * @param stateData - данные для локального хранилища
     * @returns ответ в формате, понятном платформе
     */
    getContent: (controller: BotController, stateData?: Record<string, unknown> | null) => object | string | Promise<object | string>;
    /**
     * Формирует контекст для отправки рейтинга (если поддерживается платформой).
     *
     * Используется только на платформах с поддержкой рейтинга (например, Сбер SmartApp).
     * Если рейтинг не поддерживается — метод может не реализовываться или возвращать пустой объект.
     *
     * @param controller - контроллер приложения
     * @returns данные для отправки рейтинга
     */
    getRatingContext: (controller: BotController) => object | string | Promise<object | string>;
    /**
     * Устанавливает время начала обработки запроса.
     *
     * Используется для измерения времени отклика (`processingTime`).
     *
     * @param controller - контроллер приложения
     */
    updateTimeStart: (controller: BotController) => void;
    /**
     * Возвращает время обработки запроса в миллисекундах.
     *
     * Основано на разнице между `updateTimeStart` и текущим временем.
     *
     * @param controller - контроллер приложения
     * @returns время обработки в мс
     */
    getProcessingTime: (controller: BotController) => number;
    /**
     * Уникальное имя платформы (например, 'telegram', 'alisa').
     */
    platformName: string;
    /**
     * Указывает, поддерживает ли платформа локальное хранилище.
     *
     * @param controller - контроллер приложения
     * @returns `true`, если локальное хранилище доступно
     */
    isLocalStorage: (controller: BotController) => boolean;
    /**
     * Получает данные из локального хранилища платформы.
     *
     * @param controller - контроллер приложения
     * @returns данные, сохранённые ранее
     */
    getLocalStorage: <TStorageResult = unknown>(controller: BotController) => TStorageResult | Promise<TStorageResult>;
    /**
     * Сохраняет данные в локальное хранилище платформы.
     *
     * @param data - данные для сохранения
     * @param controller - контроллер приложения
     */
    setLocalStorage: <TStorageData>(data: TStorageData, controller: BotController) => void | Promise<void>;
    /**
     * Флаг, указывающий, что платформа голосовая (например, Алиса, Маруся).
     */
    isVoice: boolean;
    /**
     * Генерирует пример входящего запроса для локального тестирования навыка/бота.
     *
     * Используется в инструментах отладки и авто-тестах.
     *
     * @param query - текст запроса
     * @param userId - идентификатор пользователя
     * @param count - счётчик для уникальности
     * @param state - состояние сессии
     * @returns объект, имитирующий входящий запрос платформы
     */
    getQueryExample: (query: string, userId: string, count: number, state: Record<string, unknown> | string) => Record<string, unknown>;
    /**
     * Отправка текста пользователю
     * Этот метод используется для активных рассылок — когда навык или бот инициирует диалог первым (например, уведомление).
     * В данном методе необходимо поддержать отправку результата пользователю.
     * Это необходимо для того, чтобы само приложение смогло продолжить диалог.
     *
     * Если ваша платформа не поддерживает отправку сообщений без входящего запроса (как Алиса), оставьте реализацию пустой или верните заглушку.
     * @param userId Ид пользователя, которому нужно отправить сообщение
     * @param controllerOrText Контроллер приложения или текст. Если необходимо отправить просто текст, можно передать строку, в случае, если необходимо передать картинку звук и тд, то необходимо корректно заполнить контроллер.
     */
    send(userId: string | number, controllerOrText: BotController | string): unknown | boolean;
    /**
     * Определяет лимит платформы.
     * В значение указывается количество запросов, которое можно отправить платформе за 1 секунду.
     * В случае если у платформы нет ограничений, можно указать 0 или null.
     * По умолчанию null
     */
    limit: number | null;
}
/**
 * Результат запроса к базе данных.
 *
 * Поддерживает доступ как по строковым, так и по числовым ключам.
 * Используется для представления результатов SELECT-запросов.
 *
 * @template TValue — тип записей в результате (например, `User`, `Session`).
 *
 * @example
 * ```ts
 * const users: IDbResult<User> = {
 *   'user:123': { id: '123', name: 'Alice' },
 *   456: { id: '456', name: 'Bob' }
 * };
 * ```
 */
export interface IDbResult<TValue = unknown> {
    /**
     * Результат запроса, доступный по строковым ключам
     */
    [keyStr: string]: TValue;
    /**
     * Результат запроса, доступный по числовым ключам
     */
    [keyInt: number]: TValue;
}
/**
 * Адаптер для работы с базой данных.
 *
 * Обеспечивает унифицированный интерфейс для различных СУБД (файловая, MongoDB, PostgreSQL и др.).
 * Все данные подключения и соединения хранятся в `AppContext` через `IDatabaseInfo`,
 * чтобы избежать повторного подключения при каждом запросе.
 */
export interface IDatabaseAdapter extends IPlugin {
    /**
     * Вызывается при удалении модели или завершении сессии.
     * Может использоваться для освобождения ресурсов, связанных с таблицей.
     * @param tableName Название таблицы, подключение к которой закрывается
     */
    close: (tableName: string) => void | Promise<void>;
    /**
     * Вызывается при завершении работы приложения или замене адаптера.
     * Используйте для закрытия соединений, сохранения данных и т.п.
     */
    destroy: () => void | Promise<void>;
    /**
     * Выполняет SELECT-запрос.
     * @param selectData Дополнительная информация для запроса. Содержит информацию о таблице и структуре.
     * @param where Сам запрос
     * @param isOne Определяет нужно ли вернуть только 1 найденную запись, либо отдать все доступные данные.
     */
    select: (selectData: IQuery, where: IQueryData | null, isOne: boolean) => Promise<IModelRes>;
    /**
     * Выполняет INSERT-запрос.
     * @param insertData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
     */
    insert: (insertData: IQuery) => Promise<boolean>;
    /**
     * Выполняет UPDATE-запрос.
     * @param updateData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
     */
    update: (updateData: IQuery) => Promise<boolean>;
    /**
     * Выполняет DELETE-запрос.
     * @param removeData Дополнительная информация для запроса. Содержит сам запроса, а также название таблицы и прочие данные.
     */
    remove: (removeData: IQuery) => Promise<boolean>;
    /**
     * Выполняет произвольный запрос через callback.
     *
     * @param callback функция обработчик
     */
    query: (callback: TQueryCb) => unknown;
    /**
     * Проверяет, установлено ли соединение с БД.
     */
    isConnected: () => Promise<boolean> | boolean;
    /**
     * Сохраняет данные (INSERT или UPDATE в зависимости от `isNew`).
     * @param query Данные для запроса. Включает как запроса, так и сами данные
     * @param isNew Флаг, говорящий о том, что добавляется новая запись
     */
    save(query: IQuery, isNew: boolean): Promise<boolean>;
    /**
     * Извлекает значения из результата запроса.
     * @param data Результат запроса. В случае успешного запроса вернутся данные, в противном случае null
     */
    getValue: (data: IModelRes) => IDbResult | null;
    /**
     * Выполняет SELECT с ограничением до одной записи.
     * @param selectData Дополнительные данные для запроса
     * @param where Сам запроса
     */
    selectOne: (selectData: IQuery, where: IQueryData | null) => Promise<IModelRes | null>;
    /**
     * Экранирует строку для безопасного использования в SQL-запросах.
     * @param str Экранируемый запрос
     */
    escapeString: (str: string | number) => string;
    /**
     * Устанавливает подключение к базе данных.
     * В случае успешного подключения возвращается true
     */
    connect: () => Promise<boolean> | boolean;
}
/**
 * Хранилище для данных подключения и состояния базы данных.
 *
 * Содержит произвольные поля, зависящие от типа БД:
 * - Для файловой БД: `{ userData: {...}, image: {...} }`
 * - Для MongoDB: `{ mongoClient: MongoClient, mongoConnect: MongoClient }`
 *
 * Используется в `AppContext<IDatabaseInfo>` для избежания повторного подключения
 * при каждом запросе.
 */
export interface IDatabaseInfo {
    [key: string]: unknown;
}
/**
 * Реестр подключённых платформ.
 *
 * Ключ — имя платформы (например, `'telegram'`), значение — её адаптер.
 */
export interface IPlatform<TQuery = unknown> {
    [name: string]: IPlatformAdapter<TQuery>;
}
/**
 * Обработчик кнопок для кастомизации отображения под платформу.
 *
 * Используется внутри адаптеров платформ для преобразования общего формата кнопок
 * в платформо-специфичный (например, для Telegram или Алисы).
 *
 * @param buttons - массив кнопок в общем формате
 * @returns результат в формате, понятном платформе
 * @example
 * ```ts
 * function buttonProcessing(buttons) {
 *     let result = ...
 *     return result;
 * }
 * ```
 */
export type TButtonProcessing<TResult = unknown, TType = Record<string, unknown> | string | null> = (buttons: IButtonType<TType>[]) => TResult;
/**
 * Данные для формирования карточки (галерея, кнопки, заголовок).
 *
 * Используется в `TCardProcessing` для кастомизации отображения под платформу.
 */
export interface ICardInfo {
    /**
     * Используется ли режим галереи (несколько карточек).
     */
    usedGallery: boolean;
    /**
     * Массив изображений для карточки(ек).
     */
    images: IImageType[];
    /**
     * Кнопки, привязанные к карточке.
     */
    buttons: Buttons;
    /**
     * Заголовок карточки.
     */
    title: string | null;
    /**
     * Описание карточки
     */
    description: string | null;
    /**
     * Показывать только первую карточку.
     */
    showOne?: boolean;
}
/**
 * Обработчик карточек для кастомизации отображения под платформу.
 *
 * @param cardInfo - данные карточки в общем формате
 * @param controller - контроллер приложения
 * @returns результат в формате, понятном платформе
 */
export type TCardProcessing<TResult = unknown> = (cardInfo: ICardInfo, controller: BotController) => TResult;
/**
 * Данные для формирования звукового ответа.
 *
 * Используется в `TSoundProcessing` для генерации TTS или аудиофайлов.
 */
export interface ISoundInfo {
    /**
     * Используется ли стандартный звуковой ответ (TTS).
     */
    usedStandardSound: boolean;
    /**
     * Текст для озвучки (если используется TTS).
     */
    text: string;
    /**
     * Список кастомных аудиофайлов.
     */
    sounds: ISound[];
}
/**
 * Обработчик звуковых сообщений.
 *
 * @param soundInfo - данные для звукового ответа
 * @param controller - контроллер приложения
 * @returns строка, массив строк или промис с медиа-контентом
 */
export type TSoundProcessing<TResult = string | Promise<string> | Promise<string[]>> = (soundInfo: ISoundInfo, controller: BotController) => TResult;
/**
 * Тип, определяющий режим работы с группировкой регулярных выражений.
 *  - auto - Режим, при котором регулярные выражения группируются при достижении определенного количества.
 *  - no-group - Режим, запрещающий группировать регулярные выражения.
 *  - group - Режим, при котором все регулярные выражения группируются.
 */
export type TCommandGroupMode = 'auto' | 'no-group' | 'group';
