/*
    ! /server/index.js
    ? Main entry point initializing Express, Socket.IO, and dynamic autoloaders for routes and actions.
*/

// % imports
import http from 'http';
import express from 'express';
import { Server as SocketServer } from 'socket.io';
import path from 'path';
import { fileURLToPath } from 'url';
import dbWorker from '../database/index.js'; // import the database worker & factory

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

/*
    = The Socket.IO Action-Autoloader:

    ^ How it works:
    ? Recursively scans the "/actions" directory.
    ? Maps all JavaScript files into a nested object structure for dot-notation access.
    ? Ignores "index.js" and files starting with a dot (e.g., ".templateAction.js").
    ? Example mapping: "/actions/chat/message.js" becomes "chat.message(socket, data)".

    ^ Usage:
    ?? Import the actions: "import actions from './actions.js';"
    ?? Trigger an action inside a connection: "socket.on('chat.message', (data) => chat.message(socket, data));"
    ? The action template expects the active "socket" and incoming "data" as arguments.
*/
import actions from './actions.js';

/*
    = The Express Router-Autoloader:

    ^ How it works:
    ? Recursively scans the "/routers" directory.
    ? Automatically generates a nested Express Router tree based on folder and file names.
    ? Ignores "index.js" and files starting with a dot (e.g., ".templateRouter.js").
    ? Files named "root.js" are mounted directly to their current directory path (/).
    ? Example mapping: "/routers/api/v1/users.js" becomes the endpoint "/api/v1/users".

    ^ Usage:
    ?? Import the master router: "import routers from './routers.js';"
    ?? Mount to Express app: "app.use('/', routers);"
    ? Each router template receives an Express sub-router instance to define its methods (GET, POST, etc.).
*/
import routers, { registeredEndpoints } from './routers.js';

// $ debug-output
console.info('[Autoloader] Loaded Actions:', Object.keys(actions));
console.info('[Autoloader] Loaded Routers:', registeredEndpoints);

// % server initialization
const app = express();
const server = http.createServer(app);

// % socket.io initialization
const io = new SocketServer(server, {
    cors: {
        origin: '*',
        methods: ['GET', 'POST']
    }
});

// % view engine & static resources
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, '..', 'frontend', 'views'));
app.use(express.static(path.join(__dirname, '..', 'frontend', 'resources')));

// % middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// $ Inject database worker into every request
app.use((req, res, next) => {
    req.db = dbWorker;
    next();
});

// % mount routers
app.use('/', routers);

// % socket.io connection handling
io.on('connection', (socket) => {
    console.log(`[Socket.IO] Client connected: ${socket.id}`);

    // $ Automatically map all loaded actions to this socket
    for (const [eventName, actionHandler] of Object.entries(actions)) {
        if (typeof actionHandler === 'function') {
            socket.on(eventName, (data) => actionHandler(socket, dbWorker, data));
        }
    }

    socket.on('disconnect', () => {
        console.log(`[Socket.IO] Client disconnected: ${socket.id}`);
    });
});

// % start server
const PORT = process.env.PORT;
server.listen(PORT, () => {
    console.log(`[Server] Express and Socket.IO are running on port ${PORT}`);
});

// $ export the server instance
export default server;