require('dotenv').config();
const express   = require('express');
const path      = require('path');
const crypto    = require('crypto');
const { v4: uuidv4 } = require('uuid');
const db        = require('./src/db');
const solutions = require('./src/solutions');
const { runTests }                          = require('./src/runner');
const { saveBaseline, compareWithBaseline,
        readBaselines, readCompares }        = require('./src/diff');

const app  = express();
const PORT = process.env.PORT || 3000;

const rateLimits = new Map();
function rateLimit(windowMs, max) {
  return (req, res, next) => {
    const key = req.ip;
    const now = Date.now();
    const record = rateLimits.get(key) || { count: 0, start: now };
    if (now - record.start > windowMs) { record.count = 0; record.start = now; }
    record.count++;
    rateLimits.set(key, record);
    if (record.count > max) return res.status(429).json({ error: 'Too many requests' });
    next();
  };
}

const sessions = new Map();
const LOGIN_USER = process.env.QA_USER || 'admin';
const LOGIN_PASS = process.env.QA_PASS || 'BugTreat2024';

function requireAuth(req, res, next) {
  const token = req.headers['x-auth-token'] || req.query.t;
  if (!token || !sessions.has(token)) return res.status(401).json({ error: 'Unauthorised' });
  sessions.set(token, Date.now());
  next();
}

app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  next();
});

app.use(express.json({ limit: '50kb' }));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/screenshots', express.static(path.join(__dirname, 'screenshots')));
app.use('/data/baselines', express.static(path.join(__dirname, 'data', 'baselines')));

app.get('/login', (req, res) => res.sendFile(path.join(__dirname, 'public', 'login.html')));

app.post('/api/login', rateLimit(15 * 60 * 1000, 10), (req, res) => {
  const { u, p } = req.body || {};
  if (!u || !p) return res.status(400).json({ error: 'Missing credentials' });
  const userOk = crypto.timingSafeEqual(Buffer.from(u.padEnd(64)), Buffer.from(LOGIN_USER.padEnd(64)));
  const passOk = crypto.timingSafeEqual(Buffer.from(p.padEnd(64)), Buffer.from(LOGIN_PASS.padEnd(64)));
  if (userOk && passOk) {
    const token = crypto.randomBytes(32).toString('hex');
    sessions.set(token, Date.now());
    return res.json({ token });
  }
  res.status(401).json({ error: 'Invalid credentials' });
});

// ── Test runs ─────────────────────────────────────────────────────────────────
app.post('/api/runs', requireAuth, rateLimit(60 * 1000, 5), (req, res) => {
  const { url, projectName, email } = req.body;
  if (!url) return res.status(400).json({ error: 'URL is required' });
  let cleanUrl = url.trim();
  if (!cleanUrl.startsWith('http')) cleanUrl = 'https://' + cleanUrl;
  try {
    const hostname = new URL(cleanUrl).hostname;
    if (/^(localhost|127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/.test(hostname))
      return res.status(400).json({ error: 'Private/internal URLs not allowed' });
  } catch { return res.status(400).json({ error: 'Invalid URL' }); }
  const id = uuidv4();
  db.insertRun(id, projectName || cleanUrl, cleanUrl, email || null);
  res.json({ id, message: 'Test started' });
  runTests(id, cleanUrl).catch(err => console.error(`Run ${id} failed:`, err.message));
});

app.get('/api/runs', requireAuth, (req, res) => res.json(db.getAllRuns()));

app.get('/api/runs/:id', requireAuth, (req, res) => {
  const run = db.getRun(req.params.id);
  if (!run) return res.status(404).json({ error: 'Not found' });
  const issues = db.getIssues(req.params.id).map(issue => ({
    ...issue,
    solution: solutions[issue.type] || { title: 'Review manually', steps: ['Open page in Chrome DevTools (F12) and investigate'] }
  }));
  res.json({ ...run, issues, pages: db.getPages(req.params.id) });
});

app.delete('/api/runs/:id', requireAuth, (req, res) => {
  db.deleteRun(req.params.id);
  res.json({ success: true });
});

// ── Visual Diff ───────────────────────────────────────────────────────────────
app.post('/api/baseline', requireAuth, (req, res) => {
  const { url, projectName } = req.body;
  if (!url) return res.status(400).json({ error: 'URL required' });
  res.json({ message: 'Saving baseline...' });
  saveBaseline(url, projectName).catch(err => console.error('Baseline failed:', err.message));
});

app.get('/api/baselines', requireAuth, (req, res) => res.json(readBaselines()));

app.post('/api/compare/:id', requireAuth, (req, res) => {
  res.json({ message: 'Comparing...' });
  compareWithBaseline(req.params.id).catch(err => console.error('Compare failed:', err.message));
});

app.get('/api/compares', requireAuth, (req, res) => res.json(readCompares()));

app.get('/report/:id', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html')));
app.get('/health', (req, res) => res.json({ status: 'ok' }));
app.get('*', (req, res) => res.sendFile(path.join(__dirname, 'public', 'index.html')));

app.listen(PORT, '0.0.0.0', () => console.log('\n✅ QA Tool running on port ' + PORT + '\n'));
