#!/usr/bin/env node
// ==========================================================================
// CARD → Granite Lookup Script (v2)
// ==========================================================================
// Queries CARD team assets endpoint (which returns full enriched records
// including ncim_discovery with EQUIP_INST_ID) for the 109 reassigned IPs
// from the findings-count investigation Appendix C.
//
// Generates:
//   docs/card-lookup-results.csv       — full CARD data for review
//   docs/granite-reassignment-upload.csv — Team_Device Loader format
//
// Usage:
//   cd backend
//   node scripts/card-granite-lookup.js
// ==========================================================================

require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });

const cardApi = require('../helpers/cardApi');
const fs      = require('fs');
const path    = require('path');

// ---------------------------------------------------------------------------
// IP → hostname mapping from Appendix C
// ---------------------------------------------------------------------------
const REASSIGNED = {
    // With approved FP workflows (58)
    '98.120.0.78': 'syn-098-120-000-078', '98.120.32.185': 'syn-098-120-032-185',
    '10.240.78.177': 'mon15-agg-sw', '10.240.78.176': 'mon16-agg-sw',
    '10.240.78.133': 'mon15-sw14', '10.240.78.130': 'mon15-sw11',
    '10.240.78.150': 'mon19-sw3', '10.240.78.107': 'mon16-sw2',
    '10.240.78.110': 'mon16-sw5', '10.240.78.106': 'mon16-sw1',
    '10.240.78.149': 'mon19-sw2', '10.240.78.154': 'mon19-sw7',
    '10.240.78.111': 'mon16-sw6', '10.240.78.153': 'mon19-sw6',
    '10.240.78.132': 'mon15-sw13', '10.240.78.115': 'mon16-sw10',
    '10.240.78.109': 'mon16-sw4', '10.240.78.112': 'mon16-sw7',
    '10.240.78.119': 'mon16-sw14', '10.240.78.114': 'mon16-sw9',
    '10.240.78.118': 'mon16-sw13', '10.240.78.117': 'mon16-sw12',
    '10.240.78.108': 'mon16-sw3', '10.240.78.155': 'mon19-sw8',
    '10.240.78.157': 'mon19-sw10', '10.240.78.151': 'mon19-sw4',
    '10.240.78.116': 'mon16-sw11', '10.240.78.152': 'mon19-sw5',
    '10.240.78.161': 'mon19-sw14', '10.240.78.160': 'mon19-sw13',
    '10.240.78.159': 'mon19-sw12', '10.240.78.158': 'mon19-sw11',
    '10.240.78.123': 'mon15-sw4', '10.240.78.137': 'mon20-sw4',
    '10.240.78.148': 'mon19-sw1', '10.240.78.125': 'mon15-sw6',
    '10.240.78.156': 'mon19-sw9', '10.241.0.63': '',
    '10.244.11.51': 'apc01se1shcc-n01-bmc', '172.27.72.1': '',
    '96.37.185.145': '', '10.240.78.170': 'mon17-sw9',
    '10.240.78.172': 'mon17-sw11', '10.240.78.169': 'mon17-sw8',
    '10.240.78.166': 'mon17-sw5', '10.240.78.174': 'mon17-sw13',
    '10.240.78.173': 'mon17-sw12', '10.240.78.167': 'mon17-sw6',
    '10.240.78.175': 'mon17-sw14', '10.240.78.168': 'mon17-sw7',
    '10.240.78.171': 'mon17-sw10', '66.61.128.10': 'syn-066-061-128-010',
    '66.61.128.233': 'apa01se1shcc-bvi101-secondary',
    '66.61.128.49': 'syn-066-061-128-049', '66.61.128.18': 'syn-066-061-128-018',
    '10.244.4.26': '', '10.244.11.5': '', '10.244.11.6': '',
    // With rejected FP workflows (8)
    '10.244.4.55': 'apc15se1shcc-n03', '10.244.11.53': 'apc01se1shcc-n03-bmc',
    '10.244.4.30': '', '10.244.11.63': 'apc04se1shcc-n01-cimc',
    '24.28.208.125': '', '24.28.210.101': 'syn-024-028-210-101',
    '10.244.11.27': '', '10.240.1.203': '',
    // Without FP workflows (43)
    '10.240.78.20': '', '172.16.1.229': '',
    '10.244.11.96': '', '10.244.11.54': 'apc02se1shcc-n01-cimc',
    '10.244.4.51': 'apc14se1shcc-n02', '10.244.11.86': '',
    '10.244.11.55': 'apc02se1shcc-n02-cimc', '24.28.208.105': 'syn-024-028-208-105',
    '10.244.4.50': 'apc14se1shcc-n01', '10.244.4.53': 'apc15se1shcc-n01',
    '10.244.11.73': 'apc07se1shcc-n02-cimc', '10.244.11.64': 'apc04se1shcc-n02-cimc',
    '10.244.4.54': 'apc15se1shcc-n02', '10.244.4.28': '',
    '10.244.11.94': '', '10.241.0.43': 'c220-wzp27340ss5',
    '10.244.11.56': 'apc02se1shcc-n03-cimc', '10.244.11.66': 'apc05se1shcc-n01-bmc',
    '10.244.4.47': 'apc13se1shcc-n01', '10.244.4.49': 'apc13se1shcc-n03',
    '10.244.4.52': 'apc14se1shcc-n03', '10.244.11.72': 'apc07se1shcc-n01-cimc',
    '10.244.4.25': 'apc02ctsbcom7-n03-cimc', '10.244.4.29': '',
    '10.244.11.74': 'apc07se1shcc-n03-cimc', '10.244.4.48': 'apc13se1shcc-n02',
    '10.244.11.65': 'apc04se1shcc-n03-cimc', '10.244.4.24': 'apc02ctsbcom7-n02-cimc',
    '10.244.11.87': '', '10.244.11.68': 'apc05se1shcc-n03-bmc',
    '10.244.11.67': 'apc05se1shcc-n02-bmc', '10.244.4.23': 'apc02ctsbcom7-n01-cimc',
    '10.244.11.57': '', '10.244.11.95': '',
    '98.120.32.145': 'syn-098-120-032-145', '98.120.0.129': 'syn-098-120-000-129',
    '68.114.184.84': 'rphy-runner-vecima',
};

const TARGET_IPS = new Set(Object.keys(REASSIGNED));

// ---------------------------------------------------------------------------
// Fetch all assets for both teams, then match against our IP list
// ---------------------------------------------------------------------------
async function fetchTeamAssets(teamName) {
    const allAssets = [];
    let page = 1;
    const pageSize = 200;

    while (true) {
        // Fetch confirmed assets (these have the richest data)
        const result = await cardApi.getTeamAssets(teamName, {
            disposition: 'confirmed',
            page,
            pageSize,
        });

        if (!result.ok) {
            console.error(`  Failed to fetch ${teamName} page ${page}: HTTP ${result.status}`);
            break;
        }

        let data;
        try { data = JSON.parse(result.body); } catch (_) { break; }

        const assets = Array.isArray(data) ? data : (data.assets || data.results || []);
        allAssets.push(...assets);

        const total = data.total || assets.length;
        console.log(`  ${teamName} page ${page}: ${assets.length} assets (total: ${total})`);

        if (allAssets.length >= total || assets.length === 0) break;
        page++;
    }

    return allAssets;
}

function extractIPFromAssetId(assetId) {
    // Asset IDs are like "10.240.78.110-CTEC" — strip the suffix
    if (!assetId) return null;
    const parts = assetId.split('-');
    // Rejoin all but the last part (the suffix like CTEC, NATL, etc.)
    // But only if the last part looks like a suffix (not a number)
    const last = parts[parts.length - 1];
    if (/^\d+$/.test(last)) return assetId; // All numeric, probably just an IP
    return parts.slice(0, -1).join('-');
}

function extractGraniteData(asset) {
    const id = asset._id || '';
    const ip = extractIPFromAssetId(id);
    const flags = (asset.card_flags && asset.card_flags[0]) || {};
    const ncim = asset.ncim_discovery || [];
    const qualys = asset.qualys_hosts || [];
    const ivanti = asset.ivanti_assets || [];
    const granite = asset.netops_granite_allips || null;
    const iseGranite = asset.ise_granite_equipment || null;

    // Extract EQUIP_INST_ID from ncim_discovery (primary source)
    let equipInstId = null;
    let graniteTeam = null;
    let entityId = null;
    let sysLocation = null;
    let ncimHostname = null;

    if (ncim.length > 0) {
        equipInstId = ncim[0].EQUIP_INST_ID || null;
        graniteTeam = ncim[0].GRANITE_RESP_TEAM || ncim[0].RESPONSIBLE_TEAM || null;
        entityId = ncim[0].ENTITYID || null;
        sysLocation = ncim[0].SYSLOCATION || null;
        ncimHostname = ncim[0].HOSTNAME || null;
    }

    // Fallback: check netops_granite_allips
    if (!equipInstId && granite && Array.isArray(granite) && granite.length > 0) {
        equipInstId = granite[0].EQUIP_INST_ID || null;
    }

    // Fallback: check ise_granite_equipment
    if (!equipInstId && iseGranite && Array.isArray(iseGranite) && iseGranite.length > 0) {
        equipInstId = iseGranite[0].EQUIP_INST_ID || null;
    }

    const hostname = ncimHostname
        || (flags.CARD_HOSTNAME && flags.CARD_HOSTNAME[0])
        || (qualys.length > 0 && qualys[0].HOSTNAME)
        || (ivanti.length > 0 && ivanti[0].hostName)
        || '';

    const confirmedTeam = asset.owner && asset.owner.confirmed
        ? asset.owner.confirmed.name : null;

    return {
        ip,
        assetId: id,
        hostname,
        equipInstId,
        graniteTeam,
        entityId,
        sysLocation,
        confirmedTeam,
        deviceId: flags.CARD_DEVICE_ID || null,
        asn: flags.CARD_ASN || null,
        vendorModel: (flags.CARD_VENDOR_MODEL || []).map(v => v.vendor_model || v).join(', '),
        status: flags.status || null,
    };
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
async function main() {
    console.log('=== CARD → Granite Lookup (v2 — team assets endpoint) ===');
    console.log(`Target IPs: ${TARGET_IPS.size}`);
    console.log(`CARD_API_URL: ${process.env.CARD_API_URL}`);
    console.log('');

    if (!cardApi.isConfigured) {
        console.error('CARD API is not configured.');
        process.exit(1);
    }

    // Fetch assets from both teams
    const teams = ['NTS-AEO-STEAM', 'NTS-AEO-ACCESS-ENG'];
    const allAssets = [];

    for (const team of teams) {
        console.log(`Fetching ${team}...`);
        const assets = await fetchTeamAssets(team);
        allAssets.push(...assets);
        console.log(`  Total: ${assets.length} assets\n`);
    }

    // Also fetch candidate/unconfirmed in case some were reassigned
    for (const team of teams) {
        for (const disp of ['candidate', 'unconfirmed']) {
            console.log(`Fetching ${team} (${disp})...`);
            try {
                const result = await cardApi.getTeamAssets(team, { disposition: disp, pageSize: 200 });
                if (result.ok) {
                    const data = JSON.parse(result.body);
                    const assets = Array.isArray(data) ? data : (data.assets || data.results || []);
                    allAssets.push(...assets);
                    console.log(`  ${assets.length} assets`);
                }
            } catch (_) { /* skip */ }
        }
    }

    console.log(`\nTotal assets fetched: ${allAssets.length}`);

    // Build IP → asset map
    const ipMap = new Map();
    for (const asset of allAssets) {
        const id = asset._id || '';
        const ip = extractIPFromAssetId(id);
        if (ip && !ipMap.has(ip)) {
            ipMap.set(ip, asset);
        }
    }

    console.log(`Unique IPs in CARD: ${ipMap.size}`);

    // Match against our target IPs
    const matched = [];
    const notFound = [];

    for (const ip of TARGET_IPS) {
        const asset = ipMap.get(ip);
        if (asset) {
            matched.push(extractGraniteData(asset));
        } else {
            notFound.push(ip);
        }
    }

    // For IPs not found in team assets, fall back to individual owner lookup
    if (notFound.length > 0) {
        console.log(`\n${notFound.length} IPs not in team assets — trying individual owner lookups...`);
        const SUFFIXES = ['CTEC', 'NATL', 'TWC', 'BHN', 'CHTR'];
        const stillNotFound = [];

        for (const ip of notFound) {
            let found = false;
            for (const suffix of SUFFIXES) {
                try {
                    const result = await cardApi.getOwner(`${ip}-${suffix}`);
                    if (result.ok) {
                        const data = JSON.parse(result.body);
                        // Owner endpoint is slim — extract what we can
                        const ncim = data.ncim_discovery || [];
                        matched.push({
                            ip,
                            assetId: data._id || `${ip}-${suffix}`,
                            hostname: REASSIGNED[ip] || '',
                            equipInstId: ncim.length > 0 ? (ncim[0].EQUIP_INST_ID || null) : null,
                            graniteTeam: ncim.length > 0 ? (ncim[0].GRANITE_RESP_TEAM || null) : null,
                            entityId: ncim.length > 0 ? (ncim[0].ENTITYID || null) : null,
                            sysLocation: ncim.length > 0 ? (ncim[0].SYSLOCATION || null) : null,
                            confirmedTeam: data.owner && data.owner.confirmed ? data.owner.confirmed.name : null,
                            deviceId: null,
                            asn: null,
                            vendorModel: '',
                            status: null,
                        });
                        found = true;
                        break;
                    }
                } catch (_) { /* continue */ }
            }
            if (!found) stillNotFound.push(ip);
        }

        if (stillNotFound.length > 0) {
            console.log(`\n${stillNotFound.length} IPs not found anywhere in CARD:`);
            stillNotFound.forEach(ip => console.log(`  ${ip} (${REASSIGNED[ip] || 'no hostname'})`));
        }
    }

    // Sort by IP
    matched.sort((a, b) => {
        const aParts = a.ip.split('.').map(Number);
        const bParts = b.ip.split('.').map(Number);
        for (let i = 0; i < 4; i++) {
            if (aParts[i] !== bParts[i]) return aParts[i] - bParts[i];
        }
        return 0;
    });

    // Summary
    const withEquipId = matched.filter(r => r.equipInstId);
    const withoutEquipId = matched.filter(r => !r.equipInstId);

    console.log('\n=== Summary ===');
    console.log(`Matched in CARD:        ${matched.length}`);
    console.log(`With EQUIP_INST_ID:     ${withEquipId.length}`);
    console.log(`Without EQUIP_INST_ID:  ${withoutEquipId.length}`);

    // Print results
    console.log('\n=== Results with EQUIP_INST_ID ===');
    console.log('IP Address           | EQUIP_INST_ID | Hostname                       | Granite Team');
    console.log('-'.repeat(100));
    for (const r of withEquipId) {
        console.log(`${r.ip.padEnd(20)} | ${String(r.equipInstId).padEnd(13)} | ${(r.hostname || '').padEnd(30)} | ${r.graniteTeam || '-'}`);
    }

    if (withoutEquipId.length > 0) {
        console.log('\n=== Results WITHOUT EQUIP_INST_ID ===');
        for (const r of withoutEquipId) {
            console.log(`  ${r.ip.padEnd(20)} ${(r.hostname || REASSIGNED[r.ip] || '').padEnd(30)} confirmed: ${r.confirmedTeam || '-'}`);
        }
    }

    // Write full CSV
    const csvPath = path.join(__dirname, '..', '..', 'docs', 'card-lookup-results.csv');
    const csvHeader = 'IP Address,CARD Asset ID,Hostname,EQUIP_INST_ID,Granite Team,Entity ID,SysLocation,Confirmed Team,Device ID,ASN,Vendor Model,Status';
    const csvRows = matched.map(r =>
        [r.ip, r.assetId, r.hostname, r.equipInstId, r.graniteTeam, r.entityId, r.sysLocation, r.confirmedTeam, r.deviceId, r.asn, r.vendorModel, r.status]
            .map(v => v === null || v === undefined ? '' : `"${String(v).replace(/"/g, '""')}"`)
            .join(',')
    );
    fs.writeFileSync(csvPath, csvHeader + '\n' + csvRows.join('\n') + '\n', 'utf8');
    console.log(`\nFull CSV: ${csvPath}`);

    // Write Granite Team_Device Loader CSV
    const graniteHeaders = [
        'DELETE', 'SET_CONFIRMED', 'EQUIPMENT CLASS', 'EQUIP_INST_ID', 'SITE_NAME',
        'EQUIP_NAME', 'EQUIP_TEMPLATE', 'EQUIP_STATUS',
        'UDA#RESPONSIBLE ORGANIZATION#RESPONSIBLE TEAM',
        'UDA#IP_ADDRESSING#IPV4_ADDRESS',
        'UDA#IP_ADDRESSING#MAC ADDRESS', 'UDA#IP_ADDRESSING#MGMT_IP_ASN', 'SERIALNUMBER',
    ];

    const graniteRows = withEquipId.map(r => [
        '',                          // DELETE
        '',                          // SET_CONFIRMED
        'S',                         // EQUIPMENT CLASS (Shelf)
        r.equipInstId,               // EQUIP_INST_ID
        '',                          // SITE_NAME
        r.hostname || REASSIGNED[r.ip] || '', // EQUIP_NAME
        '',                          // EQUIP_TEMPLATE
        '',                          // EQUIP_STATUS
        'NTS-AEO-STEAM',            // RESPONSIBLE TEAM
        r.ip,                        // IPV4_ADDRESS
        '',                          // MAC ADDRESS
        r.asn || '',                 // MGMT_IP_ASN
        r.deviceId || '',            // SERIALNUMBER
    ]);

    const granitePath = path.join(__dirname, '..', '..', 'docs', 'granite-reassignment-upload.csv');
    const graniteContent = [
        graniteHeaders.join(','),
        ...graniteRows.map(r => r.map(v => `"${String(v).replace(/"/g, '""')}"`).join(','))
    ].join('\n');
    fs.writeFileSync(granitePath, graniteContent + '\n', 'utf8');
    console.log(`Granite upload CSV (${withEquipId.length} rows): ${granitePath}`);
}

main().catch(err => {
    console.error('Unhandled error:', err);
    process.exit(1);
});
