const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');
const { v4: uuidv4 } = require('uuid');
const winston = require('winston');

// Импорт наших менеджеров
const { SessionManager } = require('./managers/SessionManager');
const { DeviceManager } = require('./managers/DeviceManager');

// Импорт роутов
const operatorsRouter = require('./routes/operators');
const adminRouter = require('./routes/admin');

// Настройка логгера
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'god-eye.log' })
  ]
});

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

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.static('public')); // Для статических файлов демо

// Инициализация менеджеров
const sessionManager = new SessionManager();
const deviceManager = new DeviceManager();

// Делаем менеджеры доступными в роутах
app.locals.sessionManager = sessionManager;
app.locals.deviceManager = deviceManager;
app.locals.logger = logger;
app.locals.io = io;

// Подключаем роуты
app.use('/api/operators', operatorsRouter);
app.use('/api/admin', adminRouter);

// Основные REST API маршруты для мониторинга
app.get('/api/status', (req, res) => {
  const deviceStats = deviceManager.getStats();
  const sessionStats = sessionManager.getStats();
  
  res.json({
    status: 'online',
    timestamp: new Date().toISOString(),
    uptime: Math.round(process.uptime()),
    devices: deviceStats,
    sessions: sessionStats
  });
});

app.get('/api/devices', (req, res) => {
  const devices = deviceManager.getConnectedDevices();
  const devicesData = devices.map(device => device.getSummary());
  
  res.json({
    success: true,
    devices: devicesData,
    total: devicesData.length
  });
});

// WebSocket обработчики
io.on('connection', (socket) => {
  logger.info(`New connection: ${socket.id}`);

  // Регистрация Android клиента
  socket.on('register:android', (data) => {
    const { deviceId, deviceInfo } = data;
    
    // Регистрируем устройство через DeviceManager
    const device = deviceManager.registerDevice(deviceId, deviceInfo, socket);
    
    logger.info(`Android client registered: ${deviceId}`, deviceInfo);
    
    // Уведомляем всех операторов о новом устройстве
    const operatorSockets = Array.from(deviceManager.operators.values())
      .filter(op => op.isConnected())
      .map(op => op.socket);
    
    operatorSockets.forEach(opSocket => {
      opSocket.emit('device:connected', { 
        deviceId, 
        deviceInfo: device.getSummary() 
      });
    });
    
    socket.emit('register:success', { deviceId });
  });

  // Регистрация оператора
  socket.on('register:operator', (data) => {
    const { operatorId, operatorInfo } = data;
    const finalOperatorId = operatorId || uuidv4();
    
    // Регистрируем оператора через DeviceManager
    const operator = deviceManager.registerOperator(finalOperatorId, operatorInfo, socket);
    
    logger.info(`Operator registered: ${finalOperatorId}`);
    
    // Отправляем список доступных устройств
    const availableDevices = deviceManager.getAvailableDevicesForOperator(finalOperatorId);
    const devicesData = availableDevices.map(device => device.getSummary());
    
    socket.emit('register:success', { 
      operatorId: finalOperatorId,
      availableDevices: devicesData
    });
  });

  // Запрос на подключение к камере (только через WebSocket для обратной совместимости)
  socket.on('camera:request', (data) => {
    const { deviceId, cameraType = 'back' } = data;
    
    // Получаем оператора из менеджера устройств
    const operator = Array.from(deviceManager.operators.values())
      .find(op => op.socket === socket);
    
    if (!operator) {
      socket.emit('camera:error', { error: 'Operator not registered' });
      return;
    }
    
    const device = deviceManager.getDevice(deviceId);
    if (!device || !device.canAcceptNewSession()) {
      socket.emit('camera:error', { error: 'Device not available' });
      return;
    }
    
    // Создаем сессию
    const session = sessionManager.createSession(deviceId, operator.operatorId, cameraType);
    
    logger.info(`Camera request: operator ${operator.operatorId} -> device ${deviceId}`);
    
    // Отправляем запрос Android клиенту
    device.socket.emit('camera:request', {
      sessionId: session.sessionId,
      operatorId: operator.operatorId,
      cameraType
    });
    
    // Добавляем сессию к участникам
    device.addSession(session.sessionId);
    operator.addSession(session.sessionId);
    
    socket.emit('camera:request-sent', { sessionId: session.sessionId, deviceId });
  });

  // Ответ от Android клиента на запрос камеры
  socket.on('camera:response', (data) => {
    const { sessionId, accepted, streamUrl, error } = data;
    const session = sessionManager.getSession(sessionId);
    
    if (!session) {
      socket.emit('camera:error', { error: 'Session not found' });
      return;
    }
    
    const operator = deviceManager.getOperator(session.operatorId);
    if (!operator || !operator.isConnected()) {
      socket.emit('camera:error', { error: 'Operator not found' });
      return;
    }
    
    if (accepted) {
      session.updateStatus('active', { streamUrl });
      logger.info(`Camera stream started: session ${sessionId}`);
      operator.socket.emit('camera:stream-ready', { sessionId, streamUrl });
    } else {
      session.updateStatus('denied', { error });
      logger.info(`Camera request denied: session ${sessionId}`, error);
      operator.socket.emit('camera:denied', { sessionId, error });
      
      // Очищаем отклоненную сессию
      const device = deviceManager.getDevice(session.deviceId);
      if (device) device.removeSession(sessionId);
      operator.removeSession(sessionId);
      sessionManager.closeSession(sessionId);
    }
  });

  // WebRTC сигнализация
  socket.on('webrtc:offer', (data) => {
    const { sessionId, offer } = data;
    const session = sessionManager.getSession(sessionId);
    
    if (session) {
      // Определяем получателя (Android -> Operator или Operator -> Android)
      const device = deviceManager.getDevice(session.deviceId);
      const operator = deviceManager.getOperator(session.operatorId);
      
      // Если отправитель - устройство, то получатель - оператор
      if (device && device.socket === socket && operator && operator.isConnected()) {
        operator.socket.emit('webrtc:offer', { sessionId, offer });
        session.updateWebRTCState('offer_sent');
      }
      // Если отправитель - оператор, то получатель - устройство
      else if (operator && operator.socket === socket && device && device.isConnected()) {
        device.socket.emit('webrtc:offer', { sessionId, offer });
        session.updateWebRTCState('offer_sent');
      }
    }
  });

  socket.on('webrtc:answer', (data) => {
    const { sessionId, answer } = data;
    const session = sessionManager.getSession(sessionId);
    
    if (session) {
      const device = deviceManager.getDevice(session.deviceId);
      const operator = deviceManager.getOperator(session.operatorId);
      
      // Если отправитель - устройство, то получатель - оператор
      if (device && device.socket === socket && operator && operator.isConnected()) {
        operator.socket.emit('webrtc:answer', { sessionId, answer });
        session.updateWebRTCState('answer_sent');
      }
      // Если отправитель - оператор, то получатель - устройство
      else if (operator && operator.socket === socket && device && device.isConnected()) {
        device.socket.emit('webrtc:answer', { sessionId, answer });
        session.updateWebRTCState('answer_sent');
      }
    }
  });

  socket.on('webrtc:ice-candidate', (data) => {
    const { sessionId, candidate } = data;
    const session = sessionManager.getSession(sessionId);
    
    if (session) {
      const device = deviceManager.getDevice(session.deviceId);
      const operator = deviceManager.getOperator(session.operatorId);
      
      // Пересылаем ICE кандидата другой стороне
      if (device && device.socket === socket && operator && operator.isConnected()) {
        operator.socket.emit('webrtc:ice-candidate', { sessionId, candidate });
      } else if (operator && operator.socket === socket && device && device.isConnected()) {
        device.socket.emit('webrtc:ice-candidate', { sessionId, candidate });
      }
    }
  });

  // Переключение типа камеры
  socket.on('camera:switch', (data) => {
    const { sessionId, cameraType } = data;
    const session = sessionManager.getSession(sessionId);
    
    if (session) {
      const device = deviceManager.getDevice(session.deviceId);
      const operator = deviceManager.getOperator(session.operatorId);
      
      // Проверяем права доступа
      if (operator && operator.socket === socket && device && device.isConnected()) {
        device.socket.emit('camera:switch', { sessionId, cameraType });
        session.switchCamera(cameraType);
        logger.info(`Camera switch requested: ${sessionId} -> ${cameraType}`);
      }
    }
  });

  // Завершение сессии
  socket.on('camera:disconnect', (data) => {
    const { sessionId } = data;
    const session = sessionManager.getSession(sessionId);
    
    if (session) {
      const device = deviceManager.getDevice(session.deviceId);
      const operator = deviceManager.getOperator(session.operatorId);
      
      // Уведомляем участников
      if (device && device.isConnected()) {
        device.socket.emit('camera:disconnect', { sessionId });
        device.removeSession(sessionId);
      }
      
      if (operator && operator.isConnected()) {
        operator.socket.emit('camera:disconnected', { sessionId });
        operator.removeSession(sessionId);
      }
      
      sessionManager.closeSession(sessionId);
      logger.info(`Camera session ended: ${sessionId}`);
    }
  });

  // Ping-Pong для проверки соединения
  socket.on('ping', (data, callback) => {
    if (callback) {
      callback({ timestamp: Date.now(), ...data });
    }
  });

  // Обработка отключения
  socket.on('disconnect', (reason) => {
    logger.info(`Client disconnected: ${socket.id}, reason: ${reason}`);
    
    // Находим устройство или оператора по сокету
    const device = Array.from(deviceManager.devices.values())
      .find(d => d.socket === socket);
    
    const operator = Array.from(deviceManager.operators.values())
      .find(op => op.socket === socket);
    
    if (device) {
      // Уведомляем операторов об отключении устройства
      const operators = deviceManager.getConnectedOperators();
      operators.forEach(op => {
        op.socket.emit('device:disconnected', { 
          deviceId: device.deviceId 
        });
      });
      
      // Завершаем активные сессии устройства
      sessionManager.closeDeviceSessions(device.deviceId);
      deviceManager.disconnectDevice(device.deviceId);
    }
    
    if (operator) {
      // Завершаем активные сессии оператора
      sessionManager.closeOperatorSessions(operator.operatorId);
      deviceManager.disconnectOperator(operator.operatorId);
    }
  });
});

// Периодическая очистка старых сессий и устройств
setInterval(() => {
  try {
    sessionManager.cleanupOldSessions(60); // Очистка сессий старше 60 минут
    deviceManager.cleanup(); // Очистка отключенных устройств
  } catch (error) {
    logger.error('Error during periodic cleanup', error);
  }
}, 5 * 60 * 1000); // Каждые 5 минут

// Обработка graceful shutdown
process.on('SIGTERM', () => {
  logger.info('SIGTERM received, shutting down gracefully');
  server.close(() => {
    logger.info('Process terminated');
    process.exit(0);
  });
});

process.on('SIGINT', () => {
  logger.info('SIGINT received, shutting down gracefully');
  server.close(() => {
    logger.info('Process terminated');
    process.exit(0);
  });
});

const PORT = process.env.PORT || 3001;

server.listen(PORT, () => {
  logger.info(`GodEye Backend Server running on port ${PORT}`);
  console.log(`🚀 Server started on http://localhost:${PORT}`);
  console.log(`📊 Admin API: http://localhost:${PORT}/api/admin/stats`);
  console.log(`👥 Operators API: http://localhost:${PORT}/api/operators/devices`);
  console.log(`📱 Status: http://localhost:${PORT}/api/status`);
  console.log(`🌐 Demo: http://localhost:${PORT}/`);
});