import fs from 'fs';
import path from 'path';
import axios from 'axios';

var merakiNetworks = [];

var config = JSON.parse(fs.readFileSync('./config/config.json'));
var devices = JSON.parse(fs.readFileSync('./enriched-meraki.csv.json'));

async function showTime() {
    for (var device of devices) {
        if (device.meraki && device.meraki.connection == "Wired" && (device.meraki.vlan == "103" || device.meraki.vlan == "143" || device.meraki.vlan == "145" || device.meraki.vlan == "310" || device.meraki.vlan == "360")) {
            console.log(`Working on ${device.device_friendly_name}.`)
            await updateSwitchPort(device.meraki.deviceSerial, device.meraki.switchport, {
                enabled: false, vlan: 340, accessPolicyType: "Sticky MAC allow list",
                stickyMacAllowList: [],
                stickyMacAllowListLimit: 1
            })
            await wait(10)
            await updateSwitchPort(device.meraki.deviceSerial, device.meraki.switchport, { enabled: true })
            //await sendMDMCommand(device.device_id, {
            //    "CommandXml": "<dict><key>RequestType</key><string>RestartDevice</string></dict>"
            //})
            //await wait(30)
            //await updateSwitchPort(device.meraki.deviceSerial, device.meraki.switchport, { enabled: true, vlan: 340 })
            //await wait(30)
            //await sendMDMCommand(device.device_id, {
            //    "CommandXml": "<dict><key>RequestType</key><string>DeviceInformation</string><key>Queries</key><array><string>Model</string><string>SerialNumber</string><string>OSVersion</string></array></dict>"
            //}
            //)
        }
    }
}

async function sendMDMCommand(deviceId, command) {

    const tokenResponse = await axios.post(
        'https://na.uemauth.workspaceone.com/connect/token',
        new URLSearchParams({
            grant_type: 'client_credentials',
            client_id: '4e43d8a448fd423984776c0b376d9047',
            client_secret: '0F858A7DC18563D057176BE1BF11A7B3',
        }),
        {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        }
    );

    const accessToken = tokenResponse.data.access_token;

    const baseUrl = `https://as1991.awmdm.com/api`;  // e.g., as1991.awmdm.com

    const headers = {
        Authorization: `Bearer ${accessToken}`,
        'aw-tenant-code': 'cMrIjIsQkKSj0N7giRG36o9OgvNHhuG4+dQGkZXm7G8=',
        Accept: 'application/json'
    };

    try {
        const response = await axios.post(
            `${baseUrl}/mdm/devices/${deviceId}/commands?command=CustomMdmCommand`,
            command,
            { headers }
        );

        console.log(`Command sent successfully: ${response.data}`);
        return response.data;
    } catch (error) {
        console.error('Error sending MDM Query:');
        if (error.response) {
            console.error('Status:', error.response.status);
            console.error('Response:', error.response.data);
        } else {
            console.error(error.message);
        }
        //throw error;
    }
}

async function updateSwitchPort(serial, portId, settings) {
    const baseUrl = 'https://api.meraki.com/api/v1';

    try {
        const response = await axios.put(
            `${baseUrl}/devices/${serial}/switch/ports/${portId}`,
            settings,
            {
                headers: {
                    'X-Cisco-Meraki-API-Key': config.auth.meraki.apiKey,
                    'Content-Type': 'application/json'
                }
            }
        );

        //console.log('Success:', response.data);
        return "Port updated.";
    } catch (error) {
        console.error('Error updating port:');
        console.error(error.response?.data || error.message);
        throw error;
    }
}

async function findMerakiNetworkId(storeNum) {
    var startTime = new Date().getTime();
    var networkSearchTerm = Number(storeNum).toString().padStart(5, "0");
    try {
        // Filter networks by partial name match (case-insensitive)
        const matchingNetworks = merakiNetworks.filter(net =>
            (net.name || '').toLowerCase().includes(networkSearchTerm.toLowerCase())
        );

        console.log(matchingNetworks)

        if (matchingNetworks.length === 0) {
            return null;
        }

        if (matchingNetworks.length > 1) {
            console.log(`Multiple networks match '${networkSearchTerm}':`);
            matchingNetworks.forEach(net => console.log(`- ${net.name} (ID: ${net.id})`));
            console.log('Using the first match...');
        }


        return matchingNetworks[0].id;
    } catch (error) {
        console.error(`Error finding Meraki Network: ${error}`);
        return null;
    }
}

async function cacheMerakiNetworks() {
    var startTime = new Date().getTime();

    try {
        let allNetworks = [];
        let nextUrl = `https://api.meraki.com/api/v1/organizations/${config.auth.meraki.orgId}/networks?perPage=1000`;

        while (nextUrl) {
            const response = await axios.get(nextUrl, {
                headers: {
                    'X-Cisco-Meraki-API-Key': config.auth.meraki.apiKey,
                    'Content-Type': 'application/json'
                }
            });

            const pageNetworks = response.data;
            allNetworks = allNetworks.concat(pageNetworks);

            //logger(`cacheMerakiNetworks()`, `Fetched ${pageNetworks.length} networks (total so far: ${allNetworks.length})`);

            // Log raw header
            //logger(`cacheMerakiNetworks()`, `Link header raw: ${response.headers.link || '(none)'}`);

            // Robust next URL extraction
            const linkHeader = response.headers.link;
            let foundNext = null;

            if (linkHeader) {
                //logger(`cacheMerakiNetworks()`, `Full Link header (raw): ${linkHeader}`);

                const parts = linkHeader.split(',');
                //logger(`cacheMerakiNetworks()`, `Split into ${parts.length} parts`);

                for (const part of parts) {
                    const trimmed = part.trim();
                    //logger(`cacheMerakiNetworks()`, `Examining part: "${trimmed}"`);

                    // Forgiving checks: lower case, no quotes required, partial match
                    const lowerTrimmed = trimmed.toLowerCase();
                    if (lowerTrimmed.includes('rel=next') || lowerTrimmed.includes('rel="next"') || lowerTrimmed.includes("rel='next'")) {
                        //logger(`cacheMerakiNetworks()`, `→ Detected rel=next in: "${trimmed}"`);

                        const urlMatch = trimmed.match(/<([^>]+)>/);
                        if (urlMatch && urlMatch[1]) {
                            foundNext = urlMatch[1].trim();  // extra trim just in case
                            //logger(`cacheMerakiNetworks()`, `→ Extracted next URL: ${foundNext}`);
                            break;
                        } else {
                            //logger(`cacheMerakiNetworks()`, `→ URL match failed on that part`);
                        }
                    }
                }
            }

            nextUrl = foundNext;

            if (!nextUrl) {
                //logger(`cacheMerakiNetworks()`, `No next page detected – ending loop`);
            } else {
                //logger(`cacheMerakiNetworks()`, `Advancing to next URL: ${nextUrl}`);
            }
        }

        merakiNetworks = allNetworks;
        logger(`cacheMerakiNetworks()`, `Cached ${merakiNetworks.length} networks. (${new Date().getTime() - startTime}ms)`);

    } catch (error) {
        console.error('Meraki Networks API error:', error.message);
        if (error.response) {
            console.error('Status:', error.response.status);
            console.error('Data:', error.response.data);
        }
    }
}

function logger(functionName, message) {
    var d = new Date();
    var year = d.getFullYear();
    var month = (d.getMonth() + 1).toString().padStart(2, "0");
    var day = d.getDate().toString().padStart(2, "0");
    let logFile = path.join(`./logs/${year}${month}${day}.log`);
    console.log(d.toLocaleString() + " " + functionName + ": " + message);
    fs.appendFileSync(logFile, d.toLocaleString() + " " + functionName + ": " + message + "\n");
}

function wait(seconds) {
    return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}

showTime();

