const express = require('express');
const cors = require('cors');
const fs = require('fs');
const path = require('path');
const morgan = require('morgan');
const crypto = require('crypto');
const axios = require('axios');
const requestIp = require('request-ip');
const geoip = require('geoip-lite');
const dotenv = require('dotenv');

const envPath = path.resolve(__dirname, '..', '.env');
if (fs.existsSync(envPath)) {
  dotenv.config({ path: envPath });
} else {
  dotenv.config();
}

const app = express();
const PORT = process.env.PORT || 4000;
const DATA_PATH = path.join(__dirname, 'data', 'devices.json');
const CONFIG_PATH = path.join(__dirname, 'config.json');

const config = loadConfig();
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || config.telegramBotToken || '';
const TELEGRAM_CHAT_ID = (process.env.TELEGRAM_CHAT_ID || config.telegramChatId || '').toString();
let telegramOffset = 0;
let telegramPollingStarted = false;

app.use(cors());
app.use(express.json());
app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public'), {
  extensions: ['html']
}));

app.use((req, res, next) => {
  res.setHeader('Cache-Control', 'no-store');
  next();
});

function loadConfig() {
  try {
    if (fs.existsSync(CONFIG_PATH)) {
      const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
      return JSON.parse(raw);
    }
  } catch (error) {
    console.warn('Could not parse config.json', error);
  }
  return {};
}

function ensureDataFile() {
  if (!fs.existsSync(DATA_PATH)) {
    fs.mkdirSync(path.dirname(DATA_PATH), { recursive: true });
    fs.writeFileSync(DATA_PATH, JSON.stringify([], null, 2));
  }
}

function readDevices() {
  ensureDataFile();
  try {
    const raw = fs.readFileSync(DATA_PATH, 'utf-8');
    const devices = JSON.parse(raw);
    return Array.isArray(devices) ? devices : [];
  } catch (err) {
    console.error('Error parsing devices.json', err);
    return [];
  }
}

function writeDevices(devices) {
  ensureDataFile();
  fs.writeFileSync(DATA_PATH, JSON.stringify(devices, null, 2));
}

function sanitizeId(input) {
  return String(input || '').trim();
}

function sanitizeIp(ip) {
  if (!ip) return '';
  if (ip.startsWith('::ffff:')) {
    return ip.replace('::ffff:', '');
  }
  return ip;
}

function lookupLocation(ip) {
  if (!ip) {
    return { country: 'N/A', region: '', city: '' };
  }
  const geo = geoip.lookup(ip);
  if (!geo) {
    return { country: 'N/A', region: '', city: '' };
  }
  return {
    country: geo.country || 'N/A',
    region: geo.region || '',
    city: geo.city || ''
  };
}

function generateTokenParts() {
  const fullToken = crypto.randomBytes(16).toString('hex');
  const mid = Math.floor(fullToken.length / 2);
  return {
    clientPart: fullToken.slice(0, mid),
    adminPart: fullToken.slice(mid)
  };
}

async function sendTelegramMessage(message, options = {}) {
  if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) {
    return;
  }
  const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`;
  try {
    const payload = {
      chat_id: TELEGRAM_CHAT_ID,
      text: message,
      disable_web_page_preview: true
    };
    if (options.parseMode) {
      payload.parse_mode = options.parseMode;
    }
    await axios.post(url, payload);
  } catch (error) {
    console.warn('Failed to send Telegram message', error.response ? error.response.data : error.message);
  }
}

async function sendTelegramNotification(message) {
  await sendTelegramMessage(message, { parseMode: 'Markdown' });
}

function formatTelegramMessage(device, verificationRequired) {
  const lines = [
    '*Nuevo registro de dispositivo*',
    `ID: ${device.deviceId}`,
    `Alias: ${device.alias || '-'}`,
    `Modelo: ${device.manufacturer} ${device.model}`,
    `Versión app: ${device.appVersionName} (${device.appVersionCode})`,
    `IP: ${device.ip || '-'} / País: ${device.country || '-'}`,
    `Última vez visto: ${device.lastSeen}`
  ];
  if (verificationRequired && device.verification) {
    lines.push('`Token Admin` (guárdalo): `' + device.verification.adminPart + '`');
    lines.push('Autorizar: `/allow ' + device.deviceId + ' TOKEN_CLIENTE`');
    lines.push('Rechazar: `/deny ' + device.deviceId + ' TOKEN_CLIENTE [motivo]`');
  }
  return lines.join('\n');
}

async function initializeTelegramOffset() {
  if (!TELEGRAM_BOT_TOKEN) {
    return;
  }
  const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getUpdates`;
  try {
    const { data } = await axios.get(url, { params: { timeout: 1 } });
    if (data.ok && Array.isArray(data.result) && data.result.length > 0) {
      telegramOffset = data.result[data.result.length - 1].update_id + 1;
    }
  } catch (error) {
    console.warn('Failed to initialize Telegram offset', error.message);
  }
}

async function pollTelegramUpdates() {
  if (!TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) {
    return;
  }
  const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getUpdates`;
  const params = {
    timeout: 25
  };
  if (telegramOffset) {
    params.offset = telegramOffset;
  }
  try {
    const { data } = await axios.get(url, { params });
    if (data.ok && Array.isArray(data.result)) {
      for (const update of data.result) {
        telegramOffset = update.update_id + 1;
        if (update.message) {
          processTelegramMessage(update.message);
        }
      }
    }
  } catch (error) {
    console.warn('Telegram polling error', error.message);
  } finally {
    setTimeout(pollTelegramUpdates, 3000);
  }
}

async function startTelegramPolling() {
  if (telegramPollingStarted || !TELEGRAM_BOT_TOKEN || !TELEGRAM_CHAT_ID) {
    return;
  }
  telegramPollingStarted = true;
  await initializeTelegramOffset();
  pollTelegramUpdates();
}

function processTelegramMessage(message) {
  const chatId = message.chat && message.chat.id ? message.chat.id.toString() : '';
  if (chatId && TELEGRAM_CHAT_ID && chatId !== TELEGRAM_CHAT_ID.toString()) {
    return;
  }
  const text = (message.text || '').trim();
  if (!text.startsWith('/')) {
    return;
  }
  const parts = text.split(/\s+/);
  const command = parts[0].toLowerCase();
  if (command === '/allow' && parts.length >= 3) {
    handleTelegramAllow(parts[1], parts[2]);
  } else if (command === '/deny' && parts.length >= 3) {
    const reason = parts.slice(3).join(' ') || 'Bloqueado desde Telegram';
    handleTelegramDeny(parts[1], parts[2], reason);
  } else if (command === '/pending') {
    handleTelegramPending();
  } else {
    sendTelegramMessage('Comandos disponibles:\n/allow <deviceId> <token_cliente>\n/deny <deviceId> <token_cliente> [motivo]\n/pending');
  }
}

async function handleTelegramAllow(deviceId, clientToken) {
  const devices = readDevices();
  const device = devices.find(d => d.deviceId === deviceId);
  if (!device || !device.verification) {
    await sendTelegramMessage(`❌ Dispositivo ${deviceId} no encontrado.`);
    return;
  }
  if (device.verification.status === 'verified') {
    await sendTelegramMessage(`ℹ️ El dispositivo ${deviceId} ya está verificado.`);
    return;
  }
  if (device.verification.clientPart !== clientToken.trim()) {
    await sendTelegramMessage('❌ Token del cliente inválido.');
    return;
  }
  device.verification.status = 'verified';
  device.verification.verifiedAt = new Date().toISOString();
  device.blocked = false;
  device.notes = '';
  writeDevices(devices);
  await sendTelegramMessage(`✅ Dispositivo ${deviceId} autorizado correctamente.`);
}

async function handleTelegramDeny(deviceId, clientToken, reason) {
  const devices = readDevices();
  const device = devices.find(d => d.deviceId === deviceId);
  if (!device || !device.verification) {
    await sendTelegramMessage(`❌ Dispositivo ${deviceId} no encontrado.`);
    return;
  }
  if (device.verification.clientPart !== clientToken.trim()) {
    await sendTelegramMessage('❌ Token del cliente inválido.');
    return;
  }
  device.verification.status = 'denied';
  device.verification.deniedAt = new Date().toISOString();
  device.blocked = true;
  device.notes = reason;
  writeDevices(devices);
  await sendTelegramMessage(`🚫 Dispositivo ${deviceId} bloqueado. Motivo: ${reason}`);
}

async function handleTelegramPending() {
  const devices = readDevices();
  const pending = devices.filter(d => !d.verification || d.verification.status !== 'verified');
  if (!pending.length) {
    await sendTelegramMessage('No hay dispositivos pendientes.');
    return;
  }
  const lines = pending.slice(0, 10).map(device => {
    const token = device.verification ? device.verification.clientPart : 'N/A';
    return `${device.deviceId} - Token cliente: ${token}`;
  });
  await sendTelegramMessage('Pendientes:\n' + lines.join('\n'));
}

app.get('/api/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

app.get('/api/devices', (req, res) => {
  const devices = readDevices();
  res.json({ devices });
});

app.post('/api/devices/register', (req, res) => {
  const {
    deviceId,
    deviceName,
    model,
    manufacturer,
    osVersion,
    appVersionName,
    appVersionCode
  } = req.body || {};

  const trimmedId = sanitizeId(deviceId);
  if (!trimmedId) {
    return res.status(400).json({ error: 'deviceId is required' });
  }

  const devices = readDevices();
  const now = new Date().toISOString();
  const clientIp = sanitizeIp(requestIp.getClientIp(req) || req.ip || '');
  const location = lookupLocation(clientIp);

  let existing = devices.find(d => d.deviceId === trimmedId);
  let isNew = false;
  if (!existing) {
    const tokenParts = generateTokenParts();
    existing = {
      deviceId: trimmedId,
      alias: '',
      deviceName: deviceName || '',
      model: model || '',
      manufacturer: manufacturer || '',
      osVersion: osVersion || '',
      appVersionName: appVersionName || '',
      appVersionCode: appVersionCode || 0,
      firstSeen: now,
      lastSeen: now,
      blocked: false,
      notes: '',
      installs: 1,
      ip: clientIp,
      country: location.country,
      verification: {
        clientPart: tokenParts.clientPart,
        adminPart: tokenParts.adminPart,
        status: 'pending',
        createdAt: now
      }
    };
    devices.push(existing);
    isNew = true;
  } else {
    existing.deviceName = deviceName || existing.deviceName;
    existing.model = model || existing.model;
    existing.manufacturer = manufacturer || existing.manufacturer;
    existing.osVersion = osVersion || existing.osVersion;
    existing.appVersionName = appVersionName || existing.appVersionName;
    existing.appVersionCode = appVersionCode || existing.appVersionCode;
    existing.lastSeen = now;
    existing.installs = (existing.installs || 1) + 1;
    existing.ip = clientIp || existing.ip;
    const hasValidCountry = location.country && location.country !== 'N/A';
    existing.country = hasValidCountry ? location.country : (existing.country || 'N/A');
    existing.verification = existing.verification || {
      ...generateTokenParts(),
      status: 'pending',
      createdAt: now
    };
  }

  const verificationRequired = !existing.verification || existing.verification.status !== 'verified';
  const blocked = existing.blocked || verificationRequired;

  writeDevices(devices);

  if (isNew || verificationRequired) {
    sendTelegramNotification(formatTelegramMessage(existing, verificationRequired));
  }

  res.json({
    blocked,
    device: existing,
    message: verificationRequired ? 'Instalación pendiente de verificación.' : (existing.notes || ''),
    verification: {
      required: verificationRequired,
      clientTokenPart: verificationRequired ? existing.verification.clientPart : ''
    }
  });
});

app.post('/api/devices/:deviceId/block', (req, res) => {
  const { deviceId } = req.params;
  const { reason } = req.body || {};
  const devices = readDevices();
  const existing = devices.find(d => d.deviceId === deviceId);
  if (!existing) {
    return res.status(404).json({ error: 'Device not found' });
  }
  existing.blocked = true;
  existing.notes = reason || existing.notes;
  existing.blockedAt = new Date().toISOString();
  writeDevices(devices);
  res.json({ blocked: true, device: existing });
});

app.post('/api/devices/:deviceId/unblock', (req, res) => {
  const { deviceId } = req.params;
  const devices = readDevices();
  const existing = devices.find(d => d.deviceId === deviceId);
  if (!existing) {
    return res.status(404).json({ error: 'Device not found' });
  }
  existing.blocked = false;
  writeDevices(devices);
  res.json({ blocked: false, device: existing });
});

app.post('/api/devices/:deviceId/verify', (req, res) => {
  const { deviceId } = req.params;
  const { clientTokenPart, adminTokenPart } = req.body || {};
  const devices = readDevices();
  const existing = devices.find(d => d.deviceId === deviceId);
  if (!existing || !existing.verification) {
    return res.status(404).json({ error: 'Device not found' });
  }
  if (!clientTokenPart || !adminTokenPart) {
    return res.status(400).json({ error: 'Both token parts are required' });
  }
  if (
    existing.verification.clientPart !== clientTokenPart.trim() ||
    existing.verification.adminPart !== adminTokenPart.trim()
  ) {
    return res.status(400).json({ error: 'Invalid token parts' });
  }
  existing.verification.status = 'verified';
  existing.verification.verifiedAt = new Date().toISOString();
  existing.blocked = false;
  writeDevices(devices);
  res.json({ verified: true, device: existing });
});

app.put('/api/devices/:deviceId/alias', (req, res) => {
  const { deviceId } = req.params;
  const { alias } = req.body || {};
  const devices = readDevices();
  const existing = devices.find(d => d.deviceId === deviceId);
  if (!existing) {
    return res.status(404).json({ error: 'Device not found' });
  }
  existing.alias = alias || '';
  writeDevices(devices);
  res.json({ device: existing });
});

app.delete('/api/devices/:deviceId', (req, res) => {
  const { deviceId } = req.params;
  const devices = readDevices();
  const filtered = devices.filter(d => d.deviceId !== deviceId);
  if (filtered.length === devices.length) {
    return res.status(404).json({ error: 'Device not found' });
  }
  writeDevices(filtered);
  res.json({ removed: true });
});

app.listen(PORT, () => {
  ensureDataFile();
  console.log(`StreamPlayer dashboard server listening on port ${PORT}`);
  startTelegramPolling();
});
