///////////////////////
// server.js
///////////////////////
require('dotenv').config();

/* =========================
   CORE IMPORTS
========================= */
const express = require('express');
const http = require('http');
const path = require('path');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const { Server } = require('socket.io');

/* =========================
   INTERNAL IMPORTS
========================= */
const pool = require('./db');
const verifyUserToken = require('./middleware/authClient');
const verifyOrgAdminToken = require('./middleware/verifyOrgAdminToken');

/* =========================
   ROUTE IMPORTS
========================= */
const adminRoutes = require('./routes/admin');
const uploadRoutes = require('./routes/uploads');
const projectRoutes = require('./routes/projects');
const taskRoutes = require('./routes/tasks');
const dashboardInvites = require('./routes/dashboardInvites');
const invites = require('./routes/invites');
const usersRoute = require('./routes/users');
//const projectRoomHistory = require('./routes/projectRoomHistory');
const chatHistoryRoutes = require('./routes/chatHistory');
const avatarRoutes = require('./routes/avatar');


const registerOrganizationRoute =
  require('./routes/organizationRegistration');

const organizationVerificationRoute =
  require('./routes/organizationVerification');

/* =========================
   APP INIT
========================= */
const app = express();
const server = http.createServer(app);

/* =========================
   SOCKET.IO INIT
========================= */
const io = new Server(server, {
  transports: ['polling', 'websocket'],
  pingTimeout: 30000,
  pingInterval: 10000,
  cors: {
    origin: 'https://saas.gocrm.net',
    methods: ['GET', 'POST'],
    credentials: true
  }
});


app.set('io', io);

// Core socket wiring (org-scoped)
require('./sockets')(io);

/* =========================
   SOCKET AUTH (JWT)
   (Socket auth is intentionally separate)
========================= */
io.use((socket, next) => {
  try {
    const token = socket.handshake.auth?.token;
    if (!token) return next(new Error('Authentication required'));

    const payload = jwt.verify(token, process.env.JWT_SECRET);

    socket.user = {
      id: payload.userId,
      organizationId: payload.organizationId,
      role: payload.role
    };

    next();
  } catch (err) {
    next(new Error('Invalid or expired token'));
  }
});

/* =========================
   SOCKET SERVICES
========================= */
require('./sockets/services/chatEvents')
  .initChatEvents({ ioInstance: io });

const { initProjectEvents } = require('./sockets/projectEvents');
initProjectEvents({ ioInstance: io });


const onlineUsers = require('./sockets/onlineUsers');

/* =========================
   EXPRESS MIDDLEWARE
========================= */
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.set('trust proxy', 1);

/* =========================
   API ROUTES (BEFORE STATIC)
========================= */

/**
 * Legacy admin-only APIs
 * (keep org-admin auth here only)
 */
app.use(
  '/admin/api',
  verifyOrgAdminToken,
  adminRoutes({ pool })
);

/**
 * Organization bootstrap flows
 */
app.use(
  '/api/organization/verify-email',
  organizationVerificationRoute({ pool })
);

app.use(
  '/register-organization',
  registerOrganizationRoute({ pool })
);

/**
 * Core application APIs (USER AUTH)
 */
app.use('/upload', uploadRoutes({ pool, io }));
app.use('/projects', verifyUserToken, projectRoutes);
app.use('/tasks', verifyUserToken, taskRoutes);
//app.use('/project-room-history', projectRoomHistory); -- deprecated
app.use('/chat', chatHistoryRoutes({ pool }));
app.use('/api/chat/history', chatHistoryRoutes({ pool }));
//app.use('/api/project-room/history', projectRoomHistory); -- deprecated

/**
 * Dashboard APIs (USER AUTH ONLY)
 */
app.use(
  '/dashboard/api',
  verifyUserToken,
  require('./routes/admin/adminProfile')({ pool })
);

app.use(
  '/dashboard/invites',
  verifyUserToken,
  dashboardInvites
);

/**
 * Public invite acceptance
 */
app.use('/invite', invites);

// get the useList 
app.use('/users', usersRoute);


/* =========================
   STATIC FILES
========================= */
app.use(express.static('public'));

app.use('/img', express.static(path.join(__dirname, 'public/img')));

const mime = require('mime-types'); // make sure this is at the top of server.js

app.use(
  '/uploads',
  express.static(path.join(__dirname, 'uploads'), {
    index: false,
    maxAge: '1d',
    setHeaders: (res, filePath) => {
      const mimeType = mime.lookup(filePath);

      if (mimeType) {
        res.setHeader('Content-Type', mimeType);
      }

      // Images need explicit, cacheable headers for <img src>
      if (mimeType && mimeType.startsWith('image/')) {
        res.setHeader('Cache-Control', 'public, max-age=86400');
      }
    }
  })
);

app.use('/api/avatars', avatarRoutes);

/* =========================
   AUTH ROUTES
========================= */

/**
 * USER LOGIN (single source of truth)
 */
app.post('/login', async (req, res) => {
  try {
    const { email, password } = req.body || {};

    // 🔒 Hard guard (prevents toLowerCase crash)
    if (!email || !password) {
      return res.json({ success: false, error: 'Invalid credentials' });
    }

    const safeEmail = String(email).trim().toLowerCase();

    const [[user]] = await pool.query(
      `
      SELECT id, organizationId, firstName, lastName, email,
             password_hash, avatar, role
      FROM users
      WHERE email = ?
      `,
      [safeEmail]
    );

    if (!user) {
      return res.json({ success: false, error: 'Invalid credentials' });
    }

    const match = await bcrypt.compare(password, user.password_hash);
    if (!match) {
      return res.json({ success: false, error: 'Invalid credentials' });
    }

    const token = jwt.sign(
      {
        userId: user.id,
        organizationId: user.organizationId,
        role: user.role
      },
      process.env.JWT_SECRET,
      { expiresIn: '12h' }
    );

    res.json({
      success: true,
      token,
      user: {
        id: user.id,
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email,
        avatar: user.avatar,
        role: user.role,
        organizationId: user.organizationId
      }
    });
  } catch (err) {
    console.error('Login error:', err);
    res.status(500).json({ success: false, error: 'Server error' });
  }
});


/**
 * Change password (USER AUTH)
 */
app.post('/changePassword', verifyUserToken, async (req, res) => {
  try {
    const { newPassword } = req.body;
    console.log ('New Password Set by user', newPassword);

    if (!newPassword || newPassword.length < 6) {
      return res.status(400).json({
        success: false,
        message: 'Password too short'
      });
    }

    const hash = await bcrypt.hash(newPassword, 10);
    await pool.query(
      'UPDATE users SET password_hash = ? WHERE id = ?',
      [hash, req.user.userId]
    );

    res.json({ success: true });

  } catch (err) {
    console.error('Password change error:', err);
    res.status(500).json({ success: false });
  }
});

async function notifyUserOfNewMessage(receiverId, payload) {

  try {

    const [rows] = await pool.query(
      'SELECT token FROM user_fcm_tokens WHERE userId = ?',
      [receiverId]
    );

    if (!rows.length) return;

    const tokens = rows.map(r => r.token);

    const message = {
      notification: {
        title: payload.senderName || 'New Message',
        body: payload.message || 'Sent you a file'
      },
      tokens
    };

    await admin.messaging().sendEachForMulticast(message);

  } catch (err) {
    console.error('FCM send error:', err);
  }
}

/* =========================
   SERVER START
========================= */
const PORT = process.env.PORT || 4600;

server.listen(PORT, '0.0.0.0', () => {
  console.log(`Server running on port ${PORT}`);
});
