const express = require('express');
const router = express.Router();
const db = require('../../database');
const { getMinecraftServers } = require('../../panel/discovery');
const { readServerProperties, writeWhitelistFile } = require('../../panel/files');
const { reloadWhitelistCommand } = require('../../panel/commands');
const { ChannelType } = require('discord.js');

// In-memory cache for RV low-bandwidth operations
let serverCache = { data: null, lastFetch: 0 };
const CACHE_TTL = 60000; // 60 seconds

// Cache for Discord channels (refresh less frequently)
let discordChannelCache = { channels: null, lastFetch: 0 };
const DISCORD_CACHE_TTL = 300000; // 5 minutes

/**
 * Get Discord channels from cache or fetch fresh
 */
async function getDiscordChannels(client) {
    const now = Date.now();
    if (discordChannelCache.channels && (now - discordChannelCache.lastFetch < DISCORD_CACHE_TTL)) {
        return discordChannelCache.channels;
    }

    const guild = client.guilds.cache.get(process.env.GUILD_ID);
    if (!guild) return [];

    const channels = guild.channels.cache.map(ch => ({
        id: ch.id,
        name: ch.name,
        type: ch.type,
        parentId: ch.parentId
    }));

    discordChannelCache = { channels, lastFetch: now };
    return channels;
}

/**
 * Check which Discord channels exist for a server
 * Returns object with missing channels array
 */
function checkServerChannels(serverName, allChannels) {
    // Extract the base name (before any " - " subtitle or parenthetical)
    // "Homestead - A Cozy Survival Experience" -> "homestead"
    // "All The Mons (Private) - TX" -> "all-the-mons"
    // "Stoneblock 4" -> "stoneblock-4"
    let baseName = serverName
        .split(' - ')[0]              // Take part before " - " subtitle
        .replace(/\s*\([^)]*\)\s*/g, '')  // Remove parentheticals like (Private)
        .toLowerCase()
        .replace(/[^a-z0-9\s]/g, '')  // Remove special chars except spaces
        .replace(/\s+/g, '-')          // Spaces to hyphens
        .replace(/-+/g, '-')           // Multiple hyphens to single
        .trim();

    // Also create a display name for voice channel matching
    const voiceDisplayName = serverName
        .split(' - ')[0]
        .replace(/\s*\([^)]*\)\s*/g, '')
        .trim();

    const expectedChannels = [
        { name: `${baseName}-chat`, type: 'text', label: 'Chat' },
        { name: `${baseName}-in-game`, type: 'text', label: 'In-Game' },
        { name: `${baseName}-forum`, type: 'forum', label: 'Forum' },
        { name: voiceDisplayName, type: 'voice', label: 'Voice' }
    ];

    const missing = [];
    const found = [];

    for (const expected of expectedChannels) {
        let exists = false;
        
        if (expected.type === 'voice') {
            // Voice channels match by exact name (case-insensitive)
            exists = allChannels.some(ch => 
                ch.type === ChannelType.GuildVoice && 
                ch.name.toLowerCase() === expected.name.toLowerCase()
            );
        } else if (expected.type === 'forum') {
            exists = allChannels.some(ch => 
                ch.type === ChannelType.GuildForum && 
                ch.name === expected.name
            );
        } else {
            // Text channels
            exists = allChannels.some(ch => 
                ch.type === ChannelType.GuildText && 
                ch.name === expected.name
            );
        }

        if (exists) {
            found.push(expected.label);
        } else {
            missing.push(expected.label);
        }
    }

    return { missing, found, complete: missing.length === 0 };
}

router.get('/', (req, res) => {
    res.render('admin/servers/index', { title: 'Server Matrix' });
});

router.get('/matrix', async (req, res) => {
    const now = Date.now();
    let serversData = [];

    // Use cache if valid, otherwise fetch fresh
    if (serverCache.data && (now - serverCache.lastFetch < CACHE_TTL)) {
        serversData = serverCache.data;
    } else {
        const discovered = await getMinecraftServers();
        
        // Fetch properties for all discovered servers sequentially to avoid rate limits
        for (const srv of discovered) {
            const props = await readServerProperties(srv.identifier);
            serversData.push({ ...srv, whitelistEnabled: props.whitelistEnabled, rawProps: props.raw });
        }
        serverCache = { data: serversData, lastFetch: now };
    }

    // Join with Database Sync Logs (Always fetch fresh logs, no cache)
    const { rows: logs } = await db.query('SELECT * FROM server_sync_log');
    const logMap = logs.reduce((acc, log) => {
        acc[log.server_identifier] = log;
        return acc;
    }, {});

    // Get Discord channels
    const client = req.app.locals.client;
    const discordChannels = await getDiscordChannels(client);

    const enrichedServers = serversData.map(srv => {
        const channelStatus = checkServerChannels(srv.name, discordChannels);
        return {
            ...srv,
            log: logMap[srv.identifier] || { is_online: false, last_error: 'Never synced' },
            discord: channelStatus
        };
    });

    // Group by Node Location
    const txServers = enrichedServers.filter(s => s.node === 'TX1');
    const ncServers = enrichedServers.filter(s => s.node === 'NC1');

    res.render('admin/servers/_matrix_body', { txServers, ncServers, layout: false });
});

router.post('/:identifier/sync', async (req, res) => {
    const { identifier } = req.params;
    try {
        const { rows: players } = await db.query(
            `SELECT minecraft_username as name, minecraft_uuid as uuid FROM users 
             JOIN subscriptions ON users.discord_id = subscriptions.discord_id 
             WHERE subscriptions.status IN ('active', 'grace_period', 'lifetime')`
        );
        
        await writeWhitelistFile(identifier, players);
        await reloadWhitelistCommand(identifier);
        
        await db.query(
            "INSERT INTO server_sync_log (server_identifier, last_successful_sync, is_online, last_error) VALUES ($1, NOW(), true, NULL) ON CONFLICT (server_identifier) DO UPDATE SET last_successful_sync = NOW(), is_online = true, last_error = NULL",
            [identifier]
        );
        res.send(`<span class="text-green-500 font-bold text-sm">✅ Synced!</span>`);
    } catch (error) {
        await db.query(
            "INSERT INTO server_sync_log (server_identifier, last_error, is_online) VALUES ($1, $2, false) ON CONFLICT (server_identifier) DO UPDATE SET last_error = $2, is_online = false",
            [identifier, error.message]
        );
        res.send(`<span class="text-red-500 font-bold text-sm">❌ Error</span>`);
    }
});

router.post('/:identifier/toggle-whitelist', async (req, res) => {
    const { identifier } = req.params;
    // Clear cache so the UI updates on next poll
    serverCache.lastFetch = 0; 
    
    const props = await readServerProperties(identifier);
    if (!props.exists) return res.send('File not found');

    let newContent;
    if (props.whitelistEnabled) {
        newContent = props.raw.replace('white-list=true', 'white-list=false');
    } else {
        if (props.raw.includes('white-list=false')) {
            newContent = props.raw.replace('white-list=false', 'white-list=true');
        } else {
            newContent = props.raw + '\nwhite-list=true';
        }
    }

    const endpoint = `${process.env.PANEL_URL}/api/client/servers/${identifier}/files/write?file=server.properties`;
    await fetch(endpoint, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${process.env.PANEL_CLIENT_KEY}`,
            'Content-Type': 'text/plain'
        },
        body: newContent
    });
    
    res.send(`<span class="text-yellow-500 font-bold text-sm">⚠️ Requires Restart</span>`);
});

// Sync all servers on a specific node
router.post('/sync-all/:node', async (req, res) => {
    const { node } = req.params;
    const nodeId = node === 'tx1' ? 3 : node === 'nc1' ? 2 : null;
    
    if (!nodeId) {
        return res.send(`<span class="text-red-500">Invalid node</span>`);
    }

    try {
        const discovered = await getMinecraftServers();
        const nodeServers = discovered.filter(s => s.nodeId === nodeId);
        
        const { rows: players } = await db.query(
            `SELECT minecraft_username as name, minecraft_uuid as uuid FROM users 
             JOIN subscriptions ON users.discord_id = subscriptions.discord_id 
             WHERE subscriptions.status IN ('active', 'grace_period', 'lifetime')`
        );

        let synced = 0;
        let errors = 0;

        for (const srv of nodeServers) {
            try {
                await writeWhitelistFile(srv.identifier, players);
                await reloadWhitelistCommand(srv.identifier);
                await db.query(
                    "INSERT INTO server_sync_log (server_identifier, last_successful_sync, is_online, last_error) VALUES ($1, NOW(), true, NULL) ON CONFLICT (server_identifier) DO UPDATE SET last_successful_sync = NOW(), is_online = true, last_error = NULL",
                    [srv.identifier]
                );
                synced++;
            } catch (err) {
                await db.query(
                    "INSERT INTO server_sync_log (server_identifier, last_error, is_online) VALUES ($1, $2, false) ON CONFLICT (server_identifier) DO UPDATE SET last_error = $2, is_online = false",
                    [srv.identifier, err.message]
                );
                errors++;
            }
        }

        res.send(`<span class="text-green-500 font-bold">✅ ${synced} synced</span>${errors > 0 ? ` <span class="text-red-500">(${errors} errors)</span>` : ''}`);
    } catch (error) {
        res.send(`<span class="text-red-500">❌ ${error.message}</span>`);
    }
});

module.exports = router;
