/**
 * Веб-сервер: локально — полный интерфейс и парсер; в облаке (CLOUD_MODE=1) — только базовая страница и API инструкций.
 * Локально: node server.js  →  http://localhost:3333
 * Облако: CLOUD_MODE=1 node server.js  →  базовая страница + GET /api/access?sheetId=...
 */
const path = require('path');
const fs = require('fs');
const { spawn } = require('child_process');
const { checkSheetAccess } = require('./admin-access');

require('dotenv').config();

process.on('uncaughtException', (err) => {
  console.error('Необработанная ошибка:', err);
});
process.on('unhandledRejection', (reason, p) => {
  console.error('Необработанный rejection:', reason);
});

const CLOUD_MODE = process.env.CLOUD_MODE === '1' || process.env.CLOUD_MODE === 'true';

// Кэш проверки доступа: раз в сутки при первом запуске, чтобы не нагружать Google API
const ACCESS_CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 часа
const accessCache = new Map(); // sheetId -> { allowed, at }

async function checkAccessCached(sheetId) {
  if (!sheetId) return false;
  const now = Date.now();
  const cached = accessCache.get(sheetId);
  if (cached && now - cached.at < ACCESS_CACHE_TTL_MS) {
    return cached.allowed;
  }
  const allowed = await checkSheetAccess(sheetId);
  accessCache.set(sheetId, { allowed, at: now });
  return allowed;
}

/**
 * Проверка доступа: если задан ACCESS_SERVER_URL — только запрос к серверу (клиент не может обойти).
 * Без разрешения сервера (id таблицы в админской таблице) парсер не запускается.
 * Кэш 24 ч. При ошибке сети или ответе не OK — доступ запрещён.
 */
async function getAccessAllowed(sheetId) {
  if (!sheetId) return false;
  const serverUrl = (process.env.ACCESS_SERVER_URL || '').trim();
  if (serverUrl) {
    const now = Date.now();
    const cached = accessCache.get(sheetId);
    if (cached && now - cached.at < ACCESS_CACHE_TTL_MS) {
      return cached.allowed;
    }
    let allowed = false;
    try {
      const base = serverUrl.replace(/\/$/, '');
      const res = await fetch(`${base}/api/access?sheetId=${encodeURIComponent(sheetId)}`);
      const data = await res.json().catch(() => ({}));
      allowed = res.ok && data.allowed === true;
    } catch (_) {
      allowed = false;
    }
    accessCache.set(sheetId, { allowed, at: now });
    return allowed;
  }
  return checkAccessCached(sheetId);
}

const express = require('express');
const app = express();
const PORT = process.env.PROGRAM_PORT || 3333;

const ROOT = __dirname;
const SETTINGS_PATH = path.join(ROOT, 'program-settings.json');

app.use(express.json());

// --- Режим облака: только базовая страница и API инструкций (без авторизации, пользователей вносишь в таблицу) ---
if (CLOUD_MODE) {
  app.get('/', (req, res) => {
    res.sendFile(path.join(ROOT, 'public', 'landing.html'));
  });
  const portableZip = path.join(ROOT, 'releases', 'ParserWB-portable.zip');
  const fallbackZip = path.join(ROOT, 'releases', 'parser-client.zip');
  const releaseZipPath = process.env.RELEASE_ZIP_PATH || (fs.existsSync(portableZip) ? portableZip : fallbackZip);
  const downloadName = releaseZipPath === portableZip ? 'ParserWB.zip' : 'parser-client.zip';
  app.get('/download', (req, res) => {
    if (!fs.existsSync(releaseZipPath)) {
      return res.status(404).send('Файл для скачивания пока не загружен.');
    }
    res.download(releaseZipPath, downloadName);
  });
  app.get('/api/access', async (req, res) => {
    const sheetId = (req.query.sheetId || '').trim();
    if (!sheetId) {
      return res.status(400).json({ error: 'sheetId required', allowed: false });
    }
    try {
      const allowed = await checkAccessCached(sheetId);
      res.json({ allowed });
    } catch (e) {
      res.status(500).json({ allowed: false, error: e.message || 'Ошибка проверки доступа' });
    }
  });

  const { getRowsForApi, updateRowForApi } = require('./server-sheets');
  app.get('/api/sheet/rows', async (req, res) => {
    const sheetId = (req.query.sheetId || '').trim();
    if (!sheetId) return res.status(400).json({ error: 'sheetId required' });
    try {
      const allowed = await checkAccessCached(sheetId);
      if (!allowed) return res.status(403).json({ error: 'Нет доступа к этой таблице' });
      const sheetTabTitle = (req.query.sheetTabTitle || '').trim() || undefined;
      const offset = parseInt(req.query.offset, 10) || 0;
      const limit = parseInt(req.query.limit, 10) || undefined;
      const sortByColumn = (req.query.sortByColumn || '').trim() || undefined;
      const sortOrder = (req.query.sortOrder || 'asc').trim();
      const rows = await getRowsForApi(sheetId, sheetTabTitle, { offset, limit, sortByColumn, sortOrder });
      res.json({ rows });
    } catch (e) {
      res.status(500).json({ error: e.message || 'Ошибка чтения таблицы' });
    }
  });
  app.post('/api/sheet/update', express.json(), async (req, res) => {
    const { sheetId, sheetTabTitle, rowIndex, Price, PriceClub, UpdatedAt } = req.body || {};
    const sid = (sheetId || '').trim();
    if (!sid) return res.status(400).json({ error: 'sheetId required' });
    const rIndex = rowIndex != null ? parseInt(rowIndex, 10) : NaN;
    if (Number.isNaN(rIndex) || rIndex < 2) return res.status(400).json({ error: 'rowIndex required (>= 2)' });
    try {
      const allowed = await checkAccessCached(sid);
      if (!allowed) return res.status(403).json({ error: 'Нет доступа к этой таблице' });
      await updateRowForApi(sid, (sheetTabTitle || '').trim() || undefined, rIndex, {
        Price: Price != null ? String(Price) : undefined,
        PriceClub: PriceClub != null ? String(PriceClub) : undefined,
        UpdatedAt: UpdatedAt != null ? String(UpdatedAt) : undefined
      });
      res.json({ ok: true });
    } catch (e) {
      res.status(500).json({ error: e.message || 'Ошибка записи в таблицу' });
    }
  });

  app.listen(PORT, () => {
    console.log(`Парсер ВБ — облачный сервер (базовая страница + API): http://localhost:${PORT}`);
  });
  return;
}

// --- Локальный режим: полный интерфейс и парсер на ПК пользователя ---
app.get('/', (req, res) => {
  res.sendFile(path.join(ROOT, 'public', 'index.html'), (err) => {
    if (err) {
      console.error(err);
      if (!res.headersSent) res.status(500).send('Ошибка загрузки страницы.');
    }
  });
});
app.use(express.static(path.join(ROOT, 'public')));

// Раздача архива парсера (чтобы «Скачать парсер» работала и без CLOUD_MODE)
const portableZip = path.join(ROOT, 'releases', 'ParserWB-portable.zip');
const fallbackZip = path.join(ROOT, 'releases', 'parser-client.zip');
const releaseZipPath = process.env.RELEASE_ZIP_PATH || (fs.existsSync(portableZip) ? portableZip : fallbackZip);
const downloadName = releaseZipPath === portableZip ? 'ParserWB.zip' : 'parser-client.zip';
app.get('/download', (req, res) => {
  if (!fs.existsSync(releaseZipPath)) {
    return res.status(404).send('Файл для скачивания пока не загружен.');
  }
  res.download(releaseZipPath, downloadName);
});

function extractSheetId(link) {
  if (!link || typeof link !== 'string') return '';
  const m = link.trim().match(/\/spreadsheets\/d\/([a-zA-Z0-9_-]+)/);
  return m ? m[1] : link.trim();
}

function loadSettings() {
  try {
    const data = fs.readFileSync(SETTINGS_PATH, 'utf8');
    return JSON.parse(data);
  } catch (e) {
    return null;
  }
}

function defaultSettings() {
  return {
    sheetLink: '',
    userDataDirYandex: '',
    userDataDirChrome: '',
    intervalMinutes: 60,
    browser: 'yandex',
    schedulerEnabled: false
  };
}

function saveSettings(settings) {
  fs.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), 'utf8');
}

// Состояние планировщика
let schedulerTimer = null;
let lastRunAt = null;
let nextRunAt = null;

function runParser() {
  const settings = loadSettings() || defaultSettings();
  const sheetId = extractSheetId(settings.sheetLink);
  const browser = settings.browser === 'chrome' ? 'chrome' : 'yandex';
  const serverUrl = (process.env.ACCESS_SERVER_URL || '').trim();
  const env = {
    ...process.env,
    SHEET_ID: sheetId,
    USER_DATA_DIR_YANDEX: settings.userDataDirYandex || '',
    USER_DATA_DIR_CHROME: settings.userDataDirChrome || '',
    ACCESS_SERVER_URL: serverUrl || '',
    // Ключ только на сервере; клиент всегда ходит через API
    GOOGLE_CREDENTIALS_PATH: '',
    // Не отправлять запросы к Google API через прокси (часто ломает парсер)
    HTTPS_PROXY: '',
    HTTP_PROXY: '',
    https_proxy: '',
    http_proxy: ''
  };

  return new Promise((resolve, reject) => {
    const child = spawn(process.execPath, [path.join(ROOT, 'src', 'index.js'), browser], {
      cwd: ROOT,
      env,
      stdio: ['ignore', 'pipe', 'pipe']
    });
    let out = '';
    let err = '';
    child.stdout.on('data', (ch) => { out += ch; });
    child.stderr.on('data', (ch) => { err += ch; });
    child.on('close', (code) => {
      lastRunAt = new Date().toISOString();
      if (code === 0) resolve({ ok: true, out, err });
      else reject(new Error(`Парсер завершился с кодом ${code}. ${err || out}`));
    });
    child.on('error', reject);
  });
}

function scheduleNext() {
  const settings = loadSettings() || defaultSettings();
  if (!settings.schedulerEnabled || schedulerTimer) return;
  const min = Math.max(1, Math.min(12000, Number(settings.intervalMinutes) || 60));
  const ms = min * 60 * 1000;
  nextRunAt = new Date(Date.now() + ms).toISOString();
  schedulerTimer = setInterval(async () => {
    nextRunAt = new Date(Date.now() + ms).toISOString();
    const settings = loadSettings() || defaultSettings();
    const sheetId = extractSheetId(settings.sheetLink);
    try {
      if (sheetId) {
        const ok = await getAccessAllowed(sheetId);
        if (!ok) {
          console.error('[scheduler] Нет доступа для этой таблицы, запуск пропущен.');
          return;
        }
      }
      await runParser();
    } catch (e) {
      console.error('[scheduler] Ошибка парсера:', e.message);
    }
  }, ms);
  console.log(`[scheduler] Запуск каждые ${min} мин. Следующий запуск: ${nextRunAt}`);
}

function stopScheduler() {
  if (schedulerTimer) {
    clearInterval(schedulerTimer);
    schedulerTimer = null;
  }
  nextRunAt = null;
  console.log('[scheduler] Остановлен');
}

// GET /api/settings
app.get('/api/settings', (req, res) => {
  const settings = loadSettings() || defaultSettings();
  res.json(settings);
});

// POST /api/settings
app.post('/api/settings', (req, res) => {
  const body = req.body || {};
  const intervalMinutes = Math.max(1, Math.min(12000, Number(body.intervalMinutes) || 60));
  const settings = {
    sheetLink: String(body.sheetLink ?? '').trim(),
    userDataDirYandex: String(body.userDataDirYandex ?? '').trim(),
    userDataDirChrome: String(body.userDataDirChrome ?? '').trim(),
    intervalMinutes,
    browser: body.browser === 'chrome' ? 'chrome' : 'yandex',
    schedulerEnabled: Boolean(body.schedulerEnabled)
  };
  saveSettings(settings);
  stopScheduler();
  if (settings.schedulerEnabled) scheduleNext();
  res.json(settings);
});

// GET /api/status
app.get('/api/status', (req, res) => {
  const settings = loadSettings() || defaultSettings();
  res.json({
    lastRunAt: lastRunAt || null,
    nextRunAt: nextRunAt || null,
    schedulerRunning: schedulerTimer != null,
    scheduleIntervalMinutes: settings.intervalMinutes,
    scheduleBrowser: settings.browser
  });
});

// POST /api/run-now — один запуск
app.post('/api/run-now', async (req, res) => {
  const settings = loadSettings() || defaultSettings();
  const sheetId = extractSheetId(settings.sheetLink);
  if (!sheetId) {
    return res.status(400).json({ error: 'Укажите ссылку на Google Таблицу в настройках.' });
  }
  try {
    const hasAccess = await getAccessAllowed(sheetId);
    if (!hasAccess) {
      return res.status(403).json({ error: 'Нет доступа для этой таблицы.' });
    }
    await runParser();
    res.json({ ok: true, message: 'Парсинг завершён.' });
  } catch (e) {
    res.status(500).json({ error: e.message || 'Ошибка запуска парсера.' });
  }
});

// POST /api/scheduler — вкл/выкл расписание
app.post('/api/scheduler', (req, res) => {
  const enabled = Boolean(req.body && req.body.enabled);
  const settings = loadSettings() || defaultSettings();
  settings.schedulerEnabled = enabled;
  saveSettings(settings);
  stopScheduler();
  if (enabled) scheduleNext();
  res.json({ schedulerEnabled: enabled });
});

const server = app.listen(PORT, () => {
  try {
    console.log(`Парсер ВБ — веб-интерфейс: http://localhost:${PORT}`);
    const settings = loadSettings() || defaultSettings();
    if (settings && settings.schedulerEnabled) scheduleNext();
    if (process.env.OPEN_BROWSER === '1') {
      const url = `http://localhost:${PORT}`;
      const { exec } = require('child_process');
      const openBrowser = () => {
        try {
          if (process.platform === 'win32') {
            exec(`start "" "${url}"`, (err) => { if (err) console.error(err); });
          } else {
            exec(`xdg-open "${url}"`, (err) => { if (err) console.error(err); });
          }
        } catch (e) { console.error(e); }
      };
      setTimeout(openBrowser, 2000);
    }
  } catch (e) {
    console.error('Ошибка при старте:', e);
  }
});
server.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Порт ${PORT} занят. Закройте предыдущий запуск парсера или измените PROGRAM_PORT в .env`);
  } else {
    console.error(err);
  }
});
