const express = require('express');
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const FileType = require('file-type');

const app = express();
const PORT = 3000;
const DATA = path.join(__dirname, 'data');
const UPLOADS = path.join(__dirname, 'uploads');

app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/uploads', express.static(UPLOADS));

const upload = multer({
  dest: UPLOADS,
  limits: {
    fileSize: 5 * 1024 * 1024 // 5 MB
  }
});

const ALLOWED_MIME_TYPES = [
  'image/png',
  'image/jpeg',
  'image/gif',
  'image/webp'
];

async function validateUploadedImage(file) {
  if (!file) return null;

  const detected = await FileType.fromFile(file.path);

  if (!detected || !ALLOWED_MIME_TYPES.includes(detected.mime)) {
    try { fs.unlinkSync(file.path); } catch (e) {}
    throw new Error('Povolené sú len PNG, JPEG, GIF a WebP obrázky do 5 MB.');
  }

  return '/uploads/' + path.basename(file.path);
}

function sanitizeNick(n) {
  return String(n || '').toLowerCase().replace(/[^a-z0-9_]/g, '').slice(0, 10);
}
function ensure(file, def=[]) {
  if (!fs.existsSync(file)) fs.writeFileSync(file, JSON.stringify(def, null, 2));
}
function read(file, def=[]) {
  ensure(file, def);
  return JSON.parse(fs.readFileSync(file, 'utf8') || '[]');
}
function write(file, data) {
  fs.writeFileSync(file, JSON.stringify(data, null, 2));
}
function usersFile() { return path.join(DATA, 'users.json'); }
function publicFile() { return path.join(DATA, 'public.json'); }
function onlineFile() { return path.join(DATA, 'online.json'); }

ensure(usersFile(), []);
ensure(publicFile(), []);
ensure(onlineFile(), {});

function updateOnline(nick) {
  const online = read(onlineFile(), {});
  online[nick] = Date.now();
  write(onlineFile(), online);
}
function getOnlineUsers() {
  const online = read(onlineFile(), {});
  const now = Date.now();
  const users = [];
  let changed = false;
  for (const [nick, ts] of Object.entries(online)) {
    if (now - ts < 30000) {
      users.push(nick);
    } else {
      delete online[nick];
      changed = true;
    }
  }
  if (changed) write(onlineFile(), online);
  return users.sort();
}
function convoFile(a, b) {
  const names = [sanitizeNick(a), sanitizeNick(b)].sort();
  return path.join(DATA, `${names[0]}_${names[1]}.json`);
}
function addMessage(file, msg) {
  const msgs = read(file, []);
  msgs.push(msg);

  while (msgs.length > 100) {
    const removed = msgs.shift();

    if (removed && removed.image) {
      const rel = String(removed.image).replace(/^\/uploads\//, '');
      const full = path.join(UPLOADS, rel);

      try {
        if (fs.existsSync(full)) {
          fs.unlinkSync(full);
        }
      } catch (err) {
        console.error('Nepodarilo sa vymazať obrázok:', full, err.message);
      }
    }
  }

  write(file, msgs);
}

app.post('/api/register', (req, res) => {
  const nick = sanitizeNick(req.body.nick);
  const password = String(req.body.password || '');
  const password2 = String(req.body.password2 || '');
  const pin = String(req.body.pin || '');
  const pin2 = String(req.body.pin2 || '');
  if (!nick || !password || password !== password2 || !pin || pin !== pin2) {
    return res.status(400).json({error:'Neplatné údaje'});
  }
  const users = read(usersFile(), []);
  if (users.find(u => u.nick === nick)) {
    return res.status(400).json({error:'Používateľ už existuje'});
  }
  users.push({ nick, password, pin });
  write(usersFile(), users);
  res.json({ok:true});
});

app.post('/api/login', (req, res) => {
  const nick = sanitizeNick(req.body.nick);
  const password = String(req.body.password || '');
  const users = read(usersFile(), []);
  const user = users.find(u => u.nick === nick && u.password === password);
  if (!user) return res.status(401).json({error:'Nesprávne prihlasovacie údaje'});
  updateOnline(nick);
  res.json({ok:true, nick});
});

app.post('/api/reset-password', (req, res) => {
  const nick = sanitizeNick(req.body.nick);
  const pin = String(req.body.pin || '');
  const password = String(req.body.password || '');
  const password2 = String(req.body.password2 || '');
  if (!nick || !pin || !password || password !== password2) {
    return res.status(400).json({error:'Neplatné údaje'});
  }
  const users = read(usersFile(), []);
  const user = users.find(u => u.nick === nick && u.pin === pin);
  if (!user) return res.status(401).json({error:'Nesprávny PIN'});
  user.password = password;
  write(usersFile(), users);
  res.json({ok:true});
});

app.post('/api/ping', (req, res) => {
  const nick = sanitizeNick(req.body.nick);
  if (nick) updateOnline(nick);
  res.json({ok:true});
});

app.get('/api/online', (req, res) => {
  const me = sanitizeNick(req.query.me);
  res.json(getOnlineUsers().filter(n => n !== me));
});

app.post('/api/logout', (req, res) => {
  const nick = sanitizeNick(req.body.nick);
  const online = read(onlineFile(), {});
  if (nick && online[nick]) {
    delete online[nick];
    write(onlineFile(), online);
  }
  res.json({ok:true});
});

app.get('/api/public', (req, res) => {
  res.json(read(publicFile(), []));
});

app.post('/api/public', upload.single('image'), async (req, res) => {
  try {
    const nick = sanitizeNick(req.body.nick);
    const text = String(req.body.text || '').slice(0, 500);
    if (!nick || (!text && !req.file)) return res.status(400).json({error:'Prázdna správa'});
    updateOnline(nick);

    const image = await validateUploadedImage(req.file);

    addMessage(publicFile(), {
      nick, text, time: Date.now(), image
    });
    res.json({ok:true});
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

app.get('/api/private/:other', (req, res) => {
  const me = sanitizeNick(req.query.me);
  const other = sanitizeNick(req.params.other);
  if (!me || !other) return res.status(400).json({error:'Neplatná prezývka'});
  res.json(read(convoFile(me, other), []));
});

app.post('/api/private/:other', upload.single('image'), async (req, res) => {
  try {
    const me = sanitizeNick(req.body.me);
    const other = sanitizeNick(req.params.other);
    const text = String(req.body.text || '').slice(0, 500);
    if (!me || !other || (!text && !req.file)) return res.status(400).json({error:'Prázdna správa'});
    updateOnline(me);

    const image = await validateUploadedImage(req.file);

    addMessage(convoFile(me, other), {
      nick: me, text, time: Date.now(), image
    });
    res.json({ok:true});
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});



const typingState = {};

app.post('/api/typing', (req, res) => {
  const nick = sanitizeNick(req.body.nick);
  const target = sanitizeNick(req.body.target || 'public');
  if (nick) {
    const key = `${target}:${nick}`;
    typingState[key] = Date.now();
  }
  res.json({ok:true});
});

app.get('/api/typing', (req, res) => {
  const me = sanitizeNick(req.query.me);
  const target = sanitizeNick(req.query.target || 'public');
  const now = Date.now();
  const active = [];

  for (const [key, ts] of Object.entries(typingState)) {
    if (now - ts > 3000) {
      delete typingState[key];
      continue;
    }
    const prefix = `${target}:`;
    if (key.startsWith(prefix)) {
      const nick = key.slice(prefix.length);
      if (nick && nick !== me) active.push(nick);
    }
  }

  res.json(active);
});

app.use((err, req, res, next) => {
  if (err && err.code === 'LIMIT_FILE_SIZE') {
    return res.status(400).json({ error: 'Maximálna veľkosť obrázka je 5 MB.' });
  }
  next(err);
});

app.listen(PORT, () => console.log(`http://localhost:${PORT}`));
