import fs from "fs";
import path from "path";
import express, {Application} from 'express';
import {createServer as httpServer} from 'http';
import {addHttpRoutes} from './utilities/Http';
import cors from 'cors';
import {Server as SocketServer} from 'socket.io';
import {TSocket} from "./utilities/Types";
import config from './utilities/Config';
import prisma from './utilities/Prisma';
import ZoneManager from "./managers/ZoneManager";
import UserManager from "./managers/UserManager";
import {Authentication} from "./middleware/Authentication";
import CommandManager from "./managers/CommandManager";
import {Dirent} from "node:fs";

export class Server
{
    private readonly app: Application;
    private readonly http: any;
    private readonly io: SocketServer;

    /**
     * Creates an instance of GameServer.
     */
    constructor() {
        this.app = express();
        this.app.use(cors());
        this.app.use(express.json());
        this.app.use(express.urlencoded({ extended: true }));
        this.http = httpServer(this.app)
        this.io = new SocketServer(this.http);
        this.io.use(Authentication)
    }

    /**
     * Start the server
     */
    public async start() {
        // Check prisma connection
        try {
            await prisma.$connect();
            console.log('[✅] Database connected');
        } catch (error: any) {
            throw new Error(`[❌] Database connection failed: ${error.message}`);
        }

        // Start the server
        try {
            await this.http.listen(config.PORT, config.HOST);
            console.log('[✅] Socket.IO running on port', config.PORT);
        } catch (error: any) {
            throw new Error(`[❌] Socket.IO failed to start: ${error.message}`);
        }

        // Add http API routes
        await addHttpRoutes(this.app);

        // Load user manager
        await UserManager.boot();

        // Load zone manager
        await ZoneManager.boot();

        // Load command manager
        // await CommandManager.boot(this.io);

        // Listen for socket connections
        this.io.on('connection', this.handleConnection.bind(this));
    }

    /**
     * Handle socket connection
     * @param socket
     * @private
     */
    private async handleConnection(socket: TSocket) {
        const eventsPath = path.join(__dirname, 'events');
        try {
            await this.loadEventHandlers(eventsPath, socket);
        } catch (error: any) {
            throw new Error('[❌] Failed to load event handlers: ' + error.message);
        }
    }

    private async loadEventHandlers(dir: string, socket: TSocket) {
        const files: Dirent[] = await fs.promises.readdir(dir, { withFileTypes: true });

        for (const file of files) {
            const fullPath = path.join(dir, file.name);

            if (file.isDirectory()) {
                await this.loadEventHandlers(fullPath, socket);
            } else if (file.isFile()) {
                const module = await import(fullPath);
                module.default(socket, this.io);
            }
        }
    }
}

// Start the server
const server = new Server();
server.start();