const express = require('express');
const cors = require('cors');
const bcrypt = require('bcryptjs'); // Movido al inicio (E)
const { PrismaClient } = require('@prisma/client');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const prisma = new PrismaClient();

app.use(cors());
app.use(express.json());

// Rate limiting simple para /api/detect (G)
const detectRateLimit = new Map();
const RATE_LIMIT_WINDOW = 1000; // 1 segundo
const RATE_LIMIT_MAX = 10; // máximo 10 requests por segundo

function checkRateLimit(ip) {
  const now = Date.now();
  const record = detectRateLimit.get(ip) || { count: 0, resetTime: now + RATE_LIMIT_WINDOW };

  if (now > record.resetTime) {
    record.count = 1;
    record.resetTime = now + RATE_LIMIT_WINDOW;
  } else {
    record.count++;
  }

  detectRateLimit.set(ip, record);
  return record.count <= RATE_LIMIT_MAX;
}

// Limpiar rate limit map cada minuto
setInterval(() => {
  const now = Date.now();
  for (const [ip, record] of detectRateLimit.entries()) {
    if (now > record.resetTime + 60000) {
      detectRateLimit.delete(ip);
    }
  }
}, 60000);

const server = http.createServer(app);
const io = new Server(server, {
  cors: {
    origin: "*",
    methods: ["GET", "POST"]
  }
});

// Hello World
app.get('/', (req, res) => {
  res.send('ALPR Backend Running');
});

const authRoutes = require('./routes/auth');
const { authenticateToken, isAdmin } = require('./middleware/auth');

app.use('/api/auth', authRoutes);

// Plates CRUD
app.get('/api/plates', authenticateToken, async (req, res) => {
  try {
    const where = req.user.role === 'ADMIN' ? {} : { addedById: req.user.id };
    const plates = await prisma.plate.findMany({
      where,
      include: { addedBy: { select: { username: true } } }
    });
    res.json(plates);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/plates', authenticateToken, async (req, res) => {
  const { number, owner } = req.body;
  const isAdm = req.user.role === 'ADMIN';
  const status = isAdm ? 'ALLOWED' : 'PENDING';

  try {
    const plate = await prisma.plate.create({
      data: {
        number,
        owner,
        status,
        addedById: req.user.id
      }
    });
    io.emit('new_plate_registered', plate);
    res.json(plate);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Admin: Approve/Reject Plate
app.put('/api/plates/:id/approve', authenticateToken, isAdmin, async (req, res) => {
  const { id } = req.params;
  const { status } = req.body;

  if (!['ALLOWED', 'DENIED'].includes(status)) {
    return res.status(400).json({ error: 'Invalid status' });
  }

  try {
    const plate = await prisma.plate.update({
      where: { id: parseInt(id) },
      data: { status }
    });
    io.emit('plate_status_updated', plate);
    res.json(plate);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Delete Plate (Admin or Owner)
app.delete('/api/plates/:id', authenticateToken, async (req, res) => {
  const { id } = req.params;
  try {
    const plate = await prisma.plate.findUnique({ where: { id: parseInt(id) } });
    if (!plate) return res.status(404).json({ error: 'Plate not found' });

    if (req.user.role !== 'ADMIN' && plate.addedById !== req.user.id) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    await prisma.plate.delete({ where: { id: parseInt(id) } });
    io.emit('plate_deleted', { id: parseInt(id) });
    res.json({ message: 'Plate deleted' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Delete Person (Admin or Owner)
app.delete('/api/people/:id', authenticateToken, async (req, res) => {
  const { id } = req.params;
  try {
    const person = await prisma.person.findUnique({ where: { id: parseInt(id) } });
    if (!person) return res.status(404).json({ error: 'Person not found' });

    if (req.user.role !== 'ADMIN' && person.addedById !== req.user.id) {
      return res.status(403).json({ error: 'Unauthorized' });
    }

    await prisma.person.delete({ where: { id: parseInt(id) } });
    io.emit('person_deleted', { id: parseInt(id) });
    res.json({ message: 'Person deleted' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// History Endpoint
app.get('/api/history', async (req, res) => {
  const { date } = req.query;
  if (!date) {
    return res.status(400).json({ error: 'Date is required' });
  }

  const startDate = new Date(date);
  startDate.setHours(0, 0, 0, 0);

  const endDate = new Date(date);
  endDate.setHours(23, 59, 59, 999);

  try {
    const logs = await prisma.accessLog.findMany({
      where: {
        timestamp: {
          gte: startDate,
          lte: endDate
        }
      },
      orderBy: { timestamp: 'desc' }
    });
    res.json(logs);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Recent Scans Endpoint (Last 5 Hours)
app.get('/api/recent', async (req, res) => {
  try {
    const fiveHoursAgo = new Date(Date.now() - 5 * 60 * 60 * 1000);
    const logs = await prisma.accessLog.findMany({
      where: {
        timestamp: { gte: fiveHoursAgo }
      },
      orderBy: { timestamp: 'desc' }
    });
    res.json(logs);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Helper: RUT Validation
function validateRut(rut) {
  if (!rut || !/^[0-9]+-[0-9kK]{1}$/.test(rut)) return false;
  let [num, dv] = rut.split('-');
  let total = 0;
  let multiple = 2;
  for (let i = num.length - 1; i >= 0; i--) {
    total += parseInt(num.charAt(i)) * multiple;
    multiple = (multiple + 1) % 8 || 2;
  }
  let res = 11 - (total % 11);
  let finalDv = res === 11 ? '0' : res === 10 ? 'K' : res.toString();
  return finalDv.toUpperCase() === dv.toUpperCase();
}

// People CRUD
app.get('/api/people', authenticateToken, async (req, res) => {
  try {
    const where = req.user.role === 'ADMIN' ? {} : { addedById: req.user.id };
    const people = await prisma.person.findMany({
      where,
      include: { addedBy: { select: { username: true } } }
    });
    res.json(people);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/people', authenticateToken, async (req, res) => {
  const { rut, name, durationDays } = req.body;
  if (!validateRut(rut)) {
    return res.status(400).json({ error: 'Invalid RUT format (12345678-K)' });
  }

  const startDate = new Date();
  const endDate = new Date();
  endDate.setDate(endDate.getDate() + parseInt(durationDays || 1));

  try {
    const person = await prisma.person.create({
      data: {
        rut,
        name,
        startDate,
        endDate,
        status: 'PENDING',
        addedById: req.user.id
      }
    });
    io.emit('new_person_registered', person);
    res.json(person);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Admin: Bulk Approve People by User
app.post('/api/people/bulk-approve', authenticateToken, isAdmin, async (req, res) => {
  const { userId } = req.body;
  try {
    await prisma.person.updateMany({
      where: { addedById: parseInt(userId), status: 'PENDING' },
      data: { status: 'APPROVED' }
    });
    io.emit('people_updated', { userId });
    res.json({ message: 'Bulk approval successful' });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// Detection Endpoint (from Python) with Rate Limiting (G)
app.post('/api/detect', async (req, res) => {
  const clientIp = req.ip || req.connection.remoteAddress;

  // Check rate limit
  if (!checkRateLimit(clientIp)) {
    return res.status(429).json({ error: 'Too many requests' });
  }

  const { plate_number } = req.body;
  console.log(`Detected: ${plate_number}`);

  const DUPLICATE_COOLDOWN_MS = 30000;

  try {
    const lastLog = await prisma.accessLog.findFirst({
      where: { plateNumber: plate_number },
      orderBy: { timestamp: 'desc' }
    });

    if (lastLog) {
      const timeDiff = new Date() - new Date(lastLog.timestamp);
      if (timeDiff < DUPLICATE_COOLDOWN_MS) {
        console.log(`Duplicate detection ignored for ${plate_number} (${timeDiff}ms since last)`);
        return res.json({ message: 'Duplicate detection ignored', ignored: true, accessStatus: lastLog.accessStatus });
      }
    }

    let plate = await prisma.plate.findUnique({
      where: { number: plate_number }
    });

    let accessStatus = 'DENIED';

    if (plate && plate.status === 'ALLOWED') {
      accessStatus = 'GRANTED';
    }

    if (!plate) {
      accessStatus = 'UNKNOWN';
    }

    const log = await prisma.accessLog.create({
      data: {
        plateNumber: plate_number,
        accessStatus,
        timestamp: new Date()
      }
    });

    io.emit('new_detection', {
      plate: plate_number,
      status: accessStatus,
      timestamp: log.timestamp
    });

    res.json({ message: 'Processed', accessStatus });

  } catch (err) {
    console.error(err);
    res.status(500).json({ error: err.message });
  }
});

// Dataset Capture Notification (from ALPR Python)
app.post('/api/dataset/capture', (req, res) => {
  const { plate_number, filename, count } = req.body;
  console.log(`📸 Dataset capture: ${plate_number} (Total: ${count})`);

  io.emit('dataset_updated', {
    plate: plate_number,
    filename,
    count,
    timestamp: new Date()
  });

  res.json({ message: 'Notification sent' });
});

// Health check endpoint
app.get('/api/health', async (req, res) => {
  try {
    await prisma.$queryRaw`SELECT 1`;
    res.json({ status: 'ok', database: 'connected' });
  } catch (err) {
    res.status(500).json({ status: 'error', database: 'disconnected' });
  }
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, async () => {
  console.log(`Server running on port ${PORT}`);

  // Seed Admin User if none exists
  try {
    const userCount = await prisma.user.count();
    if (userCount === 0) {
      console.log('No users found. Creating default admin user...');
      const hashedPassword = await bcrypt.hash('admin123', 10);
      await prisma.user.create({
        data: {
          username: 'admin',
          password: hashedPassword,
          role: 'ADMIN'
        }
      });
      console.log('Default admin created: admin / admin123');
    }
  } catch (err) {
    console.error('Error seeding admin user:', err);
  }
});
