const axios = require('axios');
const https = require('https');

class MikrotikService {
    constructor() {
        this.client = null;
    }

    getConfiguredRouterIds() {
        const ids = [1]; // Default router always exists (or assumed ID 1)
        // Scan env for MIKROTIK_HOST_X
        for (const key in process.env) {
            if (key.startsWith('MIKROTIK_HOST_')) {
                const id = parseInt(key.replace('MIKROTIK_HOST_', ''));
                if (!isNaN(id) && !ids.includes(id)) {
                    ids.push(id);
                }
            }
        }
        return ids;
    }

    getConfig(routerId = 1) {

        // Default to Router 1 if not specified or if routerId is 1.
        // If routerId is provided (e.g. 3), look for MIKROTIK_HOST_3
        let host;

        if (routerId && routerId != 1) {
            // STRICT MODE: Secondary routers MUST have their own config.
            // Do NOT fall back to main router, or we'll connect to the wrong device.
            host = process.env[`MIKROTIK_HOST_${routerId}`];
            if (!host) {
                throw new Error(`Configuration for Router ${routerId} (MIKROTIK_HOST_${routerId}) not found in .env`);
            }
        } else {
            // Default Router 1
            host = process.env.MIKROTIK_HOST;
            if (!host) throw new Error("Mikrotik Host (Router 1) not configured in .env");
        }

        const suffix = (routerId && routerId != 1) ? `_${routerId}` : '';

        // Only fallback to default credentials if specifically Router 1. 
        // For Router 3+, we expect specific creds OR we can allow fallback for creds but NOT host.
        // Actually, safer to rely on suffix or default global ONLY if logic permits.
        // Let's allow fallback for User/Pass/Port (common to reuse creds) but Host was the critical one.
        const user = process.env[`MIKROTIK_USER${suffix}`] || process.env.MIKROTIK_USER || 'admin';
        const password = process.env[`MIKROTIK_PASSWORD${suffix}`] || process.env.MIKROTIK_PASSWORD || '';
        let port = parseInt(process.env[`MIKROTIK_PORT${suffix}`] || process.env.MIKROTIK_PORT || 80);
        if (port === 8728) port = 80;

        return {
            host,
            user,
            password,
            port,
            protocol: (port === 443) ? 'https' : 'http'
        };
    }

    getClient(routerId = 1) {
        const config = this.getConfig(routerId);
        const baseURL = `${config.protocol}://${config.host}:${config.port}/rest`;

        return axios.create({
            baseURL,
            auth: {
                username: config.user,
                password: config.password
            },
            httpsAgent: new https.Agent({
                rejectUnauthorized: false
            }),
            timeout: 10000
        });
    }

    async execute(method, url, data = null, routerId = 1) {
        try {
            const client = this.getClient(routerId);
            const response = await client.request({
                method,
                url,
                data
            });
            return response.data;
        } catch (error) {
            if (error.response) {
                // throw new Error(`Mikrotik API Error: ${error.response.status} ${JSON.stringify(error.response.data)}`);
                // Return empty for 404 on list calls?
                throw new Error(`Mikrotik Error (Router ${routerId}): ${error.response.status} - ${JSON.stringify(error.response.data)}`);
            } else if (error.request) {
                console.error(`[Mikrotik Router ${routerId} No Response]`, error.message);
                throw new Error(`Mikrotik Router ${routerId} Unreachable`);
            } else {
                throw error;
            }
        }
    }

    async getSystemResource(routerId = 1) {
        const data = await this.execute('GET', '/system/resource', null, routerId);
        return Array.isArray(data) ? data[0] : data;
    }

    // --- HOTSPOT ---
    async getHotspotUsers(routerId = 1) {
        return this.execute('GET', '/ip/hotspot/user', null, routerId);
    }

    async getActiveHotspot(routerId = 1) {
        return this.execute('GET', '/ip/hotspot/active', null, routerId);
    }

    async getActiveUsers(routerId = 1) { // Alias for consistency
        return this.getActiveHotspot(routerId);
    }

    async enableUser(name, routerId = 1) {
        const users = await this.execute('GET', `/ip/hotspot/user?name=${encodeURIComponent(name)}`, null, routerId);
        if (!users || users.length === 0) throw new Error(`User ${name} not found on Router ${routerId}`);
        const id = users[0]['.id'];
        await this.execute('PATCH', `/ip/hotspot/user/${id}`, { disabled: "false" }, routerId);
    }

    async disableUser(name, routerId = 1) {
        const users = await this.execute('GET', `/ip/hotspot/user?name=${encodeURIComponent(name)}`, null, routerId);
        if (!users || users.length === 0) throw new Error(`User ${name} not found on Router ${routerId}`);
        const id = users[0]['.id'];
        await this.execute('PATCH', `/ip/hotspot/user/${id}`, { disabled: "true" }, routerId);
        // Kick
        try {
            const active = await this.execute('GET', `/ip/hotspot/active?user=${encodeURIComponent(name)}`, null, routerId);
            if (active && active.length > 0) {
                await this.execute('DELETE', `/ip/hotspot/active/${active[0]['.id']}`, null, routerId);
            }
        } catch (e) { }
    }


    // --- PPPoE ---
    async getPppoeSecrets(routerId = 1) {
        return this.execute('GET', '/ppp/secret', null, routerId);
    }

    async getActivePppoe(routerId = 1) {
        return this.execute('GET', '/ppp/active', null, routerId);
    }

    async disablePppoeUser(name, routerId = 1) {
        const users = await this.execute('GET', `/ppp/secret?name=${encodeURIComponent(name)}`, null, routerId);
        if (!users || users.length === 0) throw new Error(`User ${name} not found on Router ${routerId}`);
        const id = users[0]['.id'];
        await this.execute('PATCH', `/ppp/secret/${id}`, { disabled: "true" }, routerId);

        try {
            const active = await this.execute('GET', `/ppp/active?name=${encodeURIComponent(name)}`, null, routerId);
            if (active && active.length > 0) {
                await this.execute('DELETE', `/ppp/active/${active[0]['.id']}`, null, routerId);
            }
        } catch (e) { }
        return true;
    }

    async enablePppoeUser(name, routerId = 1) {
        const users = await this.execute('GET', `/ppp/secret?name=${encodeURIComponent(name)}`, null, routerId);
        if (!users || users.length === 0) throw new Error(`User ${name} not found on Router ${routerId}`);
        const id = users[0]['.id'];
        await this.execute('PATCH', `/ppp/secret/${id}`, { disabled: "false" }, routerId);
        return true;
    }

    async getPppoeSecret(name, routerId = 1) {
        const users = await this.execute('GET', `/ppp/secret?name=${encodeURIComponent(name)}`, null, routerId);
        return (users && users.length > 0) ? users[0] : null;
    }


    async getInterfaces(routerId = 1) {
        return this.execute('GET', '/interface', null, routerId);
    }

    async monitorInterfaceTraffic(interfaceName, routerId = 1) {
        const result = await this.execute('POST', '/interface/monitor-traffic', {
            interface: interfaceName,
            once: 'true'
        }, routerId);
        return Array.isArray(result) ? result[0] : result;
    }

    async findInterface(partialName, routerId = 1) {
        const interfaces = await this.getInterfaces(routerId);
        const exact = interfaces.find(i => i.name === partialName);
        if (exact) return exact;

        const cleanInput = partialName.toLowerCase().replace(/[^a-z0-9]/g, '');
        const fuzzy = interfaces.find(i => {
            const cleanName = i.name.toLowerCase().replace(/[^a-z0-9]/g, '');
            return cleanName.includes(cleanInput);
        });

        return fuzzy || null;
    }
}

module.exports = new MikrotikService();
