/**
 * API-сервис для Рублокс 3D (отдельный от 2D-Кубоши).
 * Бэкенд: storys-микросервис, префикс /kubikon3d/...
 */
import axios from 'axios';
import { STORYS_addres } from './API';

const api = axios.create({
    baseURL: STORYS_addres,
    timeout: 30000,
    // Поднимаем лимит размера body — без этого axios отказывается отправлять
    // payload > ~10МБ. После Этапа 3 voxel-движка RLE-сжатие даёт <2МБ
    // для 250м карты, но запас не помешает.
    maxContentLength: 100 * 1024 * 1024,   // 100 МБ
    maxBodyLength: 100 * 1024 * 1024,
});

// Подкладываем JWT в каждый запрос — storys использует его, чтобы дёрнуть
// user-микросервис и узнать имя пользователя (resolve_my_username).
// Если токена нет (гость) — заголовок не ставим, бэк отдаст пустое имя.
api.interceptors.request.use((config) => {
    try {
        const token = localStorage.getItem('Authorization');
        if (token) {
            config.headers = config.headers || {};
            config.headers.Authorization = token;
        }
    } catch (e) { /* ignore */ }
    return config;
});

// ============ ПРОЕКТЫ ============

// Save-операции с увеличенным таймаутом (120с) — для больших карт.
// Стандартный 30с таймаут вызывал ошибку «не удалось сохранить» на 250м
// карте, потому что upload 5+МБ JSON по медленной сети занимает больше.
const SAVE_TIMEOUT = 120000;

export const createProject = (userId, data) =>
    api.post('/kubikon3d/projects', { user_id: userId, ...data }, { timeout: SAVE_TIMEOUT });

/**
 * Загрузить проект по id.
 *
 * Бэкенд проверяет права доступа по правилам:
 *   - published          — открыто всем (можно вызвать без userId)
 *   - draft / review / blocked — только автору и админу
 *
 * Поэтому если открываем чужой/свой черновик в редакторе — обязательно
 * передаём userId, иначе бэк отдаст 403.
 */
export const getProject = (id, userId = null) => {
    const params = {};
    if (userId != null) params.user_id = userId;
    return api.get(`/kubikon3d/projects/${id}`, { params });
};

/**
 * Загрузить проект с retry — на случай зависшего/медленного запроса.
 *
 * ПРОБЛЕМА: иногда запрос getProject подвисает (dev-proxy, перегруженный
 * пул соединений, сетевой лаг) — и страница "Загрузка проекта… 0%"
 * замирает на 30с (таймаут axios) или до 60с (safety-timer редактора).
 * Приходилось перезагружать вручную по 5 раз.
 *
 * РЕШЕНИЕ: короткий таймаут на ПОПЫТКУ (12с) + до 3 попыток. Зависшая
 * попытка отменяется и повторяется сама, без ручной перезагрузки.
 * Сетевые/таймаут-ошибки → retry; 4xx (403/404) → сразу пробрасываем
 * (повтор не поможет).
 *
 * @param {number} id — id проекта
 * @param {number|null} userId
 * @param {number} attempts — сколько попыток (по умолчанию 3)
 * @param {number} perTryTimeout — таймаут одной попытки в мс (по умолчанию 12000)
 */
export const getProjectWithRetry = async (
    id, userId = null, attempts = 3, perTryTimeout = 12000,
) => {
    const params = {};
    if (userId != null) params.user_id = userId;
    let lastErr = null;
    for (let i = 0; i < attempts; i++) {
        try {
            return await api.get(`/kubikon3d/projects/${id}`, {
                params,
                timeout: perTryTimeout,
            });
        } catch (err) {
            lastErr = err;
            const status = err.response?.status;
            // 4xx (кроме 408/429) — повтор не имеет смысла, пробрасываем сразу.
            if (status && status >= 400 && status < 500
                && status !== 408 && status !== 429) {
                throw err;
            }
            // Сеть/таймаут/5xx — пробуем ещё раз.
            console.warn(
                `[Kubikon3DApi] getProject attempt ${i + 1}/${attempts} failed`
                + ` (${err.code || status || 'network'}), retrying...`,
            );
        }
    }
    throw lastErr;
};

export const updateProject = (id, data) =>
    api.put(`/kubikon3d/projects/${id}`, data, { timeout: SAVE_TIMEOUT });

export const deleteProject = (id, userId) =>
    api.delete(`/kubikon3d/projects/${id}`, { data: { user_id: userId } });

export const getMyProjects = (userId) =>
    api.get('/kubikon3d/my-projects', { params: { user_id: userId } });

/**
 * Лента игр Рублокса (умная лента — RUBLOX_SMART_FEED_PLAN.md).
 *
 * Второй аргумент — вкладка ленты:
 *   recommended — ранжирование по hot_score (умная лента);
 *   new         — самые свежие;
 *   popular     — по числу запусков;
 *   top_week    — топ за неделю.
 * Бэкенд принимает этот параметр и как `tab`, и как `sort` (старое имя) —
 * шлём под обоими именами, чтобы не зависеть от версии бэкенда.
 */
export const getFeed = (page = 1, tab = 'recommended', maxAge = null, minRating = null, opts = {}) =>
    api.get('/kubikon3d/feed', {
        params: {
            page, tab, sort: tab,
            ...(maxAge != null ? { max_age: maxAge } : {}),
            ...(minRating != null ? { min_rating: minRating } : {}),
            ...(opts.rank ? { rank: opts.rank } : {}),
            ...(opts.multiplayer != null
                ? { multiplayer: opts.multiplayer ? 'true' : 'false' } : {}),
            ...(opts.genre ? { genre: opts.genre } : {}),
            ...(opts.per_page ? { per_page: opts.per_page } : {}),
        },
    });

export const searchProjects = (q, maxAge = null) =>
    api.get('/kubikon3d/search', {
        params: { q, ...(maxAge != null ? { max_age: maxAge } : {}) },
    });

// ============ ПУБЛИКАЦИЯ И МОДЕРАЦИЯ (Этап 3) ============

/**
 * Опубликовать проект (умная лента — RUBLOX_SMART_FEED_PLAN.md).
 * Премодерации нет: чистая игра сразу в ленте, подозрительная → review.
 * payload: { user_id, age_rating: 6|12|16|18, title?, description?, thumbnail? }
 * Ответ: { project, review: bool, too_empty: bool }
 */
export const publishProject = (id, payload) =>
    api.post(`/kubikon3d/projects/${id}/publish`, payload);

export const unpublishProject = (id, userId) =>
    api.post(`/kubikon3d/projects/${id}/unpublish`, { user_id: userId });

/** Очередь проверки админа — игры со status='review' (подозрительные скрипты). */
export const getModerationQueue = () =>
    api.get('/kubikon3d/admin/moderation-queue');

/**
 * Решение админа по игре из очереди проверки.
 * payload: { admin_user_id, action: 'approve'|'block', comment?, age_rating? }
 */
export const moderateProject = (id, payload) =>
    api.post(`/kubikon3d/admin/projects/${id}/moderate`, payload);

/** Заблокировать игру (умная лента, Фаза 7). payload: { admin_user_id, comment? } */
export const blockProject = (id, payload) =>
    api.post(`/kubikon3d/admin/projects/${id}/block`, payload);

/** Разблокировать игру → published. payload: { admin_user_id } */
export const unblockProject = (id, payload) =>
    api.post(`/kubikon3d/admin/projects/${id}/unblock`, payload);

/** Вернуть demoted-игру в ленту вручную. payload: { admin_user_id } */
export const restoreFeed = (id, payload) =>
    api.post(`/kubikon3d/admin/projects/${id}/restore-feed`, payload);

export const getModerationHistory = (id) =>
    api.get(`/kubikon3d/projects/${id}/moderation-history`);

// ============ ПЛЕЕР / СОЦИАЛКА (Этап 3.3) ============

/** Загрузить проект для плеера. Передаём user_id для проверки доступа. */
export const getProjectForPlay = (id, userId = null, isAdmin = false) =>
    api.get(`/kubikon3d/projects/${id}`, {
        params: {
            ...(userId ? { user_id: userId } : {}),
            ...(isAdmin ? { is_admin: 'true' } : {}),
        },
    });

export const incrementPlay = (id, userId) =>
    api.post(`/kubikon3d/projects/${id}/play`,
        userId ? { user_id: userId } : {});

/** kind: 'like' | 'dislike'. Toggle: повторный голос того же типа снимает,
 *  голос другого типа — переключает. */
export const toggleLike = (id, userId, kind = 'like') =>
    api.post(`/kubikon3d/projects/${id}/like`, { user_id: userId, kind });

export const getLikeStatus = (id, userId) =>
    api.get(`/kubikon3d/projects/${id}/like-status`, { params: { user_id: userId } });

/** payload: { reporter_user_id, target_type, target_id, category, text } */
export const createReport = (payload) =>
    api.post('/kubikon3d/reports', payload);

/** Публичные игры автора. */
export const getUserGames = (userId, maxAge = null) =>
    api.get(`/kubikon3d/users/${userId}/games`, {
        params: maxAge != null ? { max_age: maxAge } : {},
    });

// ============ ЛИДЕРБОРД (рекорды прохождения игр) ============

/** Топ-N рекордов прохождения проекта. По умолчанию 5. */
export const getLeaderboard = (projectId, limit = 5) =>
    api.get(`/kubikon3d/projects/${projectId}/leaderboard`, {
        params: { limit },
    });

/** Отправить рекорд прохождения. Если время лучше предыдущего — обновляется. */
export const submitLeaderboard = (projectId, userId, timeMs) =>
    api.post(`/kubikon3d/projects/${projectId}/leaderboard`, {
        user_id: userId,
        time_ms: timeMs,
    });

// ============ ИЗБРАННОЕ ============
export const toggleFavorite = (projectId, userId) =>
    api.post(`/kubikon3d/projects/${projectId}/favorite`, { user_id: userId });
export const getFavoriteStatus = (projectId, userId) =>
    api.get(`/kubikon3d/projects/${projectId}/favorite-status`,
        { params: { user_id: userId } });
export const getMyFavorites = (userId) =>
    api.get(`/kubikon3d/users/${userId}/favorites`);

// ============ ИСТОРИЯ ============
export const getPlayHistory = (userId, limit = 8) =>
    api.get(`/kubikon3d/users/${userId}/history`, { params: { limit } });

// ============ ТРЕНДЫ / ТОП-АВТОРЫ / АКТИВНОСТЬ ============
export const getTrending = (limit = 8) =>
    api.get('/kubikon3d/trending', { params: { limit } });
export const getTopAuthors = (limit = 10) =>
    api.get('/kubikon3d/top-authors', { params: { limit } });
export const getActivity = (limit = 10) =>
    api.get('/kubikon3d/activity', { params: { limit } });
export const getCollections = () =>
    api.get('/kubikon3d/collections');
export const getEvents = () =>
    api.get('/kubikon3d/events');

// ============ PERF LOGS ============
export const submitPerfLog = (sample) =>
    api.post('/kubikon3d/perf-log', sample);

// ============ БАГ-РЕПОРТЫ РУБЛОКСА (с прикреплением скриншота) ============

/**
 * Создать баг-репорт. Использует multipart/form-data, потому что может нести файл.
 * fields: { bug_type, title, message, url_path, user_agent, user_id?, project_id?, screenshot? File }
 */
export const createBugReport = (fields) => {
    const fd = new FormData();
    Object.entries(fields).forEach(([k, v]) => {
        if (v == null || v === '') return;
        fd.append(k, v);
    });
    return api.post('/kubikon3d/bug-reports', fd, {
        headers: { 'Content-Type': 'multipart/form-data' },
    });
};

export const getAdminBugReports = (status = 'open', limit = 100) =>
    api.get('/kubikon3d/admin/bug-reports', { params: { status, limit } });

export const updateAdminBugReport = (id, payload) =>
    api.post(`/kubikon3d/admin/bug-reports/${id}`, payload);

// ============ HEARTBEAT / ОНЛАЙН ============

export const playHeartbeat = (sessionId, projectId, userId = null) =>
    api.post('/kubikon3d/play/heartbeat', {
        session_id: sessionId,
        project_id: projectId,
        user_id: userId,
    });

export const getOnline = () =>
    api.get('/kubikon3d/admin/online');

// ============ DASHBOARD / СТАТИСТИКА ============

export const getDashboard = () =>
    api.get('/kubikon3d/admin/dashboard');

export const getAdminAllGames = (status = 'all', sort = 'new', limit = 200) =>
    api.get('/kubikon3d/admin/all-games', { params: { status, sort, limit } });

export const getAdminAuthors = (limit = 100) =>
    api.get('/kubikon3d/admin/authors', { params: { limit } });

// ============ ЖАЛОБЫ (АДМИНКА) ============

export const getAdminReports = (status = 'open', limit = 200) =>
    api.get('/kubikon3d/admin/reports', { params: { status, limit } });

export const resolveAdminReport = (id, status, adminUserId) =>
    api.post(`/kubikon3d/admin/reports/${id}`, { status, admin_user_id: adminUserId });

// ============ БАН ПУБЛИКАЦИЙ (этап 3.10) ============

/** Публичный — узнать активный бан публикаций пользователя. */
export const getPublishBanStatus = (userId) =>
    api.get(`/kubikon3d/users/${userId}/publish-ban-status`);
export const getPublishBanHistory = (userId) =>
    api.get(`/kubikon3d/admin/users/${userId}/publish-ban-history`);

// ============ КОММЕНТАРИИ ПОД ИГРАМИ (этап 3.9) ============

export const getProjectComments = (projectId) =>
    api.get(`/kubikon3d/projects/${projectId}/comments`);

/** payload: { user_id, username, text } */
export const createProjectComment = (projectId, payload) =>
    api.post(`/kubikon3d/projects/${projectId}/comments`, payload);

export const deleteProjectComment = (commentId, userId) =>
    api.delete(`/kubikon3d/comments/${commentId}`, { data: { user_id: userId } });

export const editProjectComment = (commentId, userId, text) =>
    api.put(`/kubikon3d/comments/${commentId}`, { user_id: userId, text });

// Админ
export const getAdminComments = (filter = 'flagged', projectId = null, limit = 200) =>
    api.get('/kubikon3d/admin/comments', {
        params: { filter, ...(projectId ? { project: projectId } : {}), limit },
    });

// ============ ВНУТРИИГРОВОЙ ЧАТ (этап 3.5/3.6) ============

/** since (опц.) — id последнего сообщения; если задан — вернётся только дельта. */
export const getChat = (projectId, since = null, limit = 50) =>
    api.get(`/kubikon3d/projects/${projectId}/chat`, {
        params: { ...(since ? { since } : {}), limit },
    });

/** payload: { user_id, username, text } */
export const postChatMessage = (projectId, payload) =>
    api.post(`/kubikon3d/projects/${projectId}/chat`, payload);

/** Узнать активный мьют чата для пользователя. */
export const getChatMuteStatus = (userId) =>
    api.get(`/kubikon3d/users/${userId}/chat-mute-status`);

// Админ
export const getAdminChatMessages = (filter = 'flagged', projectId = null, limit = 200) =>
    api.get('/kubikon3d/admin/chat/messages', {
        params: { filter, ...(projectId ? { project: projectId } : {}), limit },
    });

export const getAdminChatBans = (filter = 'active', limit = 200) =>
    api.get('/kubikon3d/admin/chat/bans', { params: { filter, limit } });

// ============================================================================
// Пользовательские модели (Этап 1+ редактора моделей)
// ============================================================================
// Эндпоинты для воксельных и гладких моделей, созданных пользователями.
// Отображаются в Toolbox в разделах "Мои модели" и "Сообщество".
// См. KUBIKON_MODEL_EDITOR_PLAN.md.

/** Создать модель.
 *  payload: { title, kind: 'voxel'|'smooth', model_data: stringJSON,
 *             description?, thumbnail_b64? }
 *  Возвращает serialize_full (с model_data).
 */
export const createUserModel = (userId, payload) =>
    api.post('/kubikon3d/models', { user_id: userId, ...payload },
        { timeout: SAVE_TIMEOUT });

/** Загрузить модель по id. userId — для проверки доступа к приватной. */
export const getUserModel = (id, userId = null) => {
    const params = {};
    if (userId != null) params.user_id = userId;
    return api.get(`/kubikon3d/models/${id}`, { params });
};

/** Обновить модель. payload: { title?, description?, model_data?, thumbnail_b64? } */
export const updateUserModel = (id, userId, payload) =>
    api.put(`/kubikon3d/models/${id}`, { user_id: userId, ...payload },
        { timeout: SAVE_TIMEOUT });

export const deleteUserModel = (id, userId) =>
    api.delete(`/kubikon3d/models/${id}`, {
        data: { user_id: userId },
        params: { user_id: userId },
    });

/** Мои модели (для раздела "Мои" в Toolbox). */
export const getMyUserModels = (userId, opts = {}) =>
    api.get('/kubikon3d/models/mine', {
        params: {
            user_id: userId,
            ...(opts.kind ? { kind: opts.kind } : {}),
            ...(opts.limit ? { limit: opts.limit } : {}),
            ...(opts.offset ? { offset: opts.offset } : {}),
        },
    });

/** Публичные модели (для раздела "Сообщество" в Toolbox).
 *  opts: { q, kind, limit, offset, userId }
 *  userId — чтобы у моделей пришёл флаг liked_by_me (подсветка лайка). */
export const getPublicUserModels = (opts = {}) =>
    api.get('/kubikon3d/models/public', {
        params: {
            ...(opts.q ? { q: opts.q } : {}),
            ...(opts.kind ? { kind: opts.kind } : {}),
            ...(opts.limit ? { limit: opts.limit } : {}),
            ...(opts.offset ? { offset: opts.offset } : {}),
            ...(opts.userId != null ? { user_id: opts.userId } : {}),
        },
    });

export const publishUserModel = (id, userId) =>
    api.post(`/kubikon3d/models/${id}/publish`, { user_id: userId });

export const unpublishUserModel = (id, userId) =>
    api.post(`/kubikon3d/models/${id}/unpublish`, { user_id: userId });

/** Инкремент uses_count — вызывать когда модель ставят в проект. */
export const incrementModelUses = (id) =>
    api.post(`/kubikon3d/models/${id}/use`);

/** Поставить/снять лайк пользовательской модели (toggle).
 *  Возвращает { liked, likes_count }. */
export const likeUserModel = (id, userId) =>
    api.post(`/kubikon3d/models/${id}/like`, { user_id: userId });

// ============ СКИНЫ ИГРОКА (R15) ============

/** Список купленных скинов игрока. Возвращает { skins: [...], count }. */
export const getOwnedSkins = (userId) =>
    api.get('/kubikon3d/rublox/owned-skins', { params: { user_id: userId } });

/** Выбранный (надетый) скин игрока. Возвращает { user_id, skin_folder }.
 *  Если записи нет — бэк отдаёт дефолт skin_bacon-hair. */
export const getEquippedSkin = (userId) =>
    api.get('/kubikon3d/rublox/equipped-skin', { params: { user_id: userId } });

/** Установить выбранный скин игрока. Бэк проверяет владение скином.
 *  Возвращает { ok, skin_folder } или ошибку. */
export const setEquippedSkin = (userId, skinFolder) =>
    api.post('/kubikon3d/rublox/equipped-skin', {
        user_id: userId, skin_folder: skinFolder,
    });
