import fs from 'fs';
import csv from 'csv-parser';
import path from 'path';
import axios from 'axios';
import { match } from 'assert';

var merakiNetworks = [];


var config = JSON.parse(fs.readFileSync('./config/config.json'));



// Assume you have these functions from your previous code
// e.g., findMerakiNetworkId(storeNum) → returns network ID
// getMerakiClients(networkId) → returns array of clients with description, vlan, switchport, status, etc.

async function processCSVAndEnrichMeraki(csvFilePath) {
  const results = [];

  // Step 1: Parse the CSV
  await new Promise((resolve, reject) => {
    fs.createReadStream(csvFilePath)
      .pipe(csv())
      .on('data', (row) => results.push(row))
      .on('end', resolve)
      .on('error', reject);
  });

  // Step 2: Process each row
  const enrichedData = [];
  for (const row of results) {
    const friendlyName = row.device_friendly_name?.trim() || '';  // Column 2

    if (!friendlyName) continue;

    // Extract store number: e.g., "US003897MSCAERIE" → "003897"
    const storeMatch = friendlyName.match(/(\d{6})/);  // Pull 6 digits
    const storeNum = storeMatch ? storeMatch[0] : null;

    if (!storeNum) {
      console.log(`Skipping row with invalid friendlyName: ${friendlyName}`);
      continue;
    }

    try {
      // Step 3: Find Meraki Network ID
      const networkId = await findMerakiNetworkId(storeNum);

      if (!networkId) {
        console.log(`No Meraki network found for store: ${storeNum}`);
        enrichedData.push({ ...row, meraki: null });
        continue;
      }

      // Step 4: Get Meraki clients for the network
      const clients = await getMerakiClients(networkId);

      // Step 5: Find matching client by description === friendlyName (case-insensitive)
      const matchingClient = clients.find(client => 
        client.description?.toUpperCase().trim() === friendlyName.toUpperCase()
      );

      if (!matchingClient) {
        console.log(`No matching Meraki client for device: ${friendlyName} in store ${storeNum}`);
        enrichedData.push({ ...row, meraki: null });
        continue;
      } else {
        console.log(matchingClient)
      }

      // Step 6: Extract relevant Meraki info (device, port, vlan, port status)
      const merakiInfo = {
        deviceName: matchingClient.recentDeviceName || 'N/A',
        deviceSerial: matchingClient.recentDeviceSerial || 'N/A',
        switchport: matchingClient.switchport || 'N/A',
        vlan: matchingClient.vlan || 'N/A',
        status: matchingClient.status || 'Unknown',
        connection: matchingClient.recentDeviceConnection || 'N/A',
        lastSeen: matchingClient.lastSeen || 'N/A',
        ip: matchingClient.ip || 'N/A',
        mac: matchingClient.mac || 'N/A'
        // Add more fields as needed, e.g., portStatus if available from another API
      };

      // Note: If "port status" requires another Meraki call (e.g., get switch ports), add it here:
      // const ports = await getMerakiSwitchPorts(matchingClient.recentDeviceSerial);
      // Then find the port matching switchport and get its status (enabled, link, etc.)

      enrichedData.push({ ...row, meraki: merakiInfo });
    } catch (error) {
      console.error(`Error processing store ${storeNum}, device ${friendlyName}:`, error.message);
      enrichedData.push({ ...row, meraki: null, error: error.message });
    }
  }

  // Step 7: Return or save the enriched data (e.g., as JSON)
  return enrichedData;
}

async function getMerakiClients(merakiNetwork) {
  if (merakiNetwork) {
    try {
      let allClients = [];
      let nextUrl = `https://api.meraki.com/api/v1/networks/${merakiNetwork}/clients?perPage=5000&timespan=2592000`;  // Start with high perPage + 24h window

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

        const pageClients = await response.data;
        allClients = allClients.concat(pageClients);

        //console.log(`Fetched ${pageClients.length} clients from this page (total so far: ${allClients.length})`);

        // Check Link header for next page
        const linkHeader = await response.headers.link;
        if (!linkHeader) {
          nextUrl = null;
          break;
        }

        // Parse the 'next' link from Link header (format: <url>; rel="next", ...)
        const nextMatch = await linkHeader.match(/<([^>]+)>;\s*rel="next"/);
        nextUrl = nextMatch ? nextMatch[1] : null;
      }
      return allClients;
    } catch (error) {
      logger(`getMerakiClients(${merakiNetwork})`, `Error: ${error}`)
      return null;
    }
  } else {
    return [];
  }
}

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())
    );

    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() {

  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);
    }
  }
}

async function rebootWorkspaceOneDevice(deviceId) {
  const baseUrl = `https://${awHost}/api`;  // e.g., as123.awmdm.com
  const headers = {
    'Authorization': `Basic ${Buffer.from('your_username:your_password').toString('base64')}`, // or use API key method
    'aw-tenant-code': tenantCode,
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  };

  try {
    const response = await axios.post(
      `${baseUrl}/mdm/devices/${deviceId}/commands`,
      { Command: 'RebootDevice' },  // or 'RestartDevice' for iOS/tvOS
      { headers }
    );

    console.log('Reboot command sent successfully:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error sending reboot:');
    console.error(error.response?.data || error.message);
    throw error;
  }
}
/*
async function updateSwitchPort(networkId, serial, portId, settings) {
  const apiKey = 'YOUR_MERAKI_API_KEY_HERE';
  const baseUrl = 'https://api.meraki.com/api/v1';

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

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

// Usage examples
updateSwitchPort('L_123456789012345678', 'Q3LU-ABCDE-12345', '8', false);   // Disable port 8
// updateSwitchPort('L_123456789012345678', 'Q3LU-ABCDE-12345', '8', true);   // Enable port 8*/
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");
}

async function sendMdmQuery(deviceId, apiKey, tenantCode, awHost) {
  const baseUrl = `https://${awHost}/api`;  // e.g., as1991.awmdm.com

  const headers = {
    'Authorization': `Basic ${Buffer.from('your_username:your_password').toString('base64')}`, // or API key method
    'aw-tenant-code': tenantCode,
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  };

  try {
    const response = await axios.post(
      `${baseUrl}/mdm/devices/${deviceId}/commands`,
      { Command: 'QueryDevice' },
      { headers }
    );

    console.log('Query 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;
  }
}

// Usage example
const csvFile = 'appleTV.csv';
await cacheMerakiNetworks();
processCSVAndEnrichMeraki(csvFile)
  .then(enriched => {
    //console.log('Enriched Data:', JSON.stringify(enriched, null, 2));
    // Optional: save to file
    fs.writeFileSync('enriched-meraki.csv.json', JSON.stringify(enriched, null, 2));
  })
  .catch(err => console.error('Fatal error:', err));