#!/usr/bin/env node
/**
 * Query Omada Controller firewall rules for Blockscout access
 * Uses direct API calls with admin credentials
 */

import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Load environment variables
dotenv.config({ path: join(__dirname, '..', '.env') });

const BLOCKSCOUT_IP = '192.168.11.140';
const BLOCKSCOUT_PORT = '80';

// Get configuration from environment
const controllerUrl = process.env.OMADA_CONTROLLER_URL || process.env.OMADA_CONTROLLER_BASE_URL || 'https://192.168.11.8:8043';
const username = process.env.OMADA_ADMIN_USERNAME || process.env.OMADA_API_KEY || process.env.OMADA_CLIENT_ID;
const password = process.env.OMADA_ADMIN_PASSWORD || process.env.OMADA_API_SECRET || process.env.OMADA_CLIENT_SECRET;
const siteId = process.env.OMADA_SITE_ID || '090862bebcb1997bb263eea9364957fe';
const verifySSL = process.env.OMADA_VERIFY_SSL !== 'false';

// Disable SSL verification if needed
if (!verifySSL) {
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}

async function login() {
  const url = `${controllerUrl}/api/v2/login`;
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      username: username,
      password: password,
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Login failed: ${response.status} ${response.statusText} - ${errorText}`);
  }

  const data = await response.json();
  
  if (data.errorCode !== 0) {
    throw new Error(`Login failed: ${data.msg || 'Unknown error'}`);
  }

  const token = data.result?.token || data.token;
  if (!token) {
    throw new Error('No token received from server');
  }

  return token;
}

async function requestWithToken(token, endpoint, method = 'GET', body = null) {
  const url = `${controllerUrl}${endpoint}`;
  
  const options = {
    method,
    headers: {
      'Content-Type': 'application/json',
      'Csrf-Token': token,
      'Cookie': `TOKEN=${token}`,
    },
  };

  if (body) {
    options.body = JSON.stringify(body);
  }

  const response = await fetch(url, options);

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
  }

  const data = await response.json();
  
  if (data.errorCode !== 0) {
    throw new Error(`API error: ${data.msg || 'Unknown error'}`);
  }

  return data.result || data;
}

async function main() {
  console.log('════════════════════════════════════════');
  console.log('Omada Firewall Rules Check for Blockscout');
  console.log('════════════════════════════════════════');
  console.log('');
  console.log(`Controller: ${controllerUrl}`);
  console.log(`Site ID: ${siteId}`);
  console.log(`Blockscout IP: ${BLOCKSCOUT_IP}`);
  console.log(`Blockscout Port: ${BLOCKSCOUT_PORT}`);
  console.log('');

  if (!username || !password) {
    console.error('❌ Missing Omada credentials in .env file');
    console.error('');
    console.error('Required environment variables:');
    console.error('  OMADA_ADMIN_USERNAME (or OMADA_API_KEY/OMADA_CLIENT_ID)');
    console.error('  OMADA_ADMIN_PASSWORD (or OMADA_API_SECRET/OMADA_CLIENT_SECRET)');
    console.error('');
    console.error('Optional:');
    console.error('  OMADA_CONTROLLER_URL (default: https://192.168.11.8:8043)');
    console.error('  OMADA_SITE_ID (default: auto-detect)');
    process.exit(1);
  }

  try {
    console.log('Authenticating to Omada Controller...');
    const token = await login();
    console.log('✓ Authentication successful');
    console.log('');

    console.log('Fetching firewall rules...');
    const rules = await requestWithToken(token, `/api/v2/sites/${siteId}/firewall/rules`);
    console.log(`✓ Found ${rules.length} firewall rules`);
    console.log('');

    // Filter rules that might affect Blockscout
    const relevantRules = rules.filter((rule) => {
      const affectsBlockscoutIP = 
        !rule.dstIp || 
        rule.dstIp === BLOCKSCOUT_IP || 
        rule.dstIp.includes(BLOCKSCOUT_IP.split('.').slice(0, 3).join('.')) ||
        rule.dstIp === '192.168.11.0/24' ||
        rule.dstIp === '192.168.11.0/255.255.255.0';

      const affectsPort80 = 
        !rule.dstPort || 
        rule.dstPort === BLOCKSCOUT_PORT || 
        rule.dstPort.includes(BLOCKSCOUT_PORT) ||
        rule.dstPort === 'all' ||
        rule.dstPort === '0-65535';

      const isTCP = 
        !rule.protocol || 
        rule.protocol === 'tcp' || 
        rule.protocol === 'tcp/udp' || 
        rule.protocol === 'all';

      return rule.enable && (affectsBlockscoutIP || affectsPort80) && isTCP;
    });

    if (relevantRules.length > 0) {
      console.log(`🔍 Found ${relevantRules.length} rule(s) that might affect Blockscout:`);
      console.log('');

      relevantRules.forEach((rule) => {
        console.log(`Rule: ${rule.name || rule.id}`);
        console.log(`  ID: ${rule.id}`);
        console.log(`  Enabled: ${rule.enable ? 'Yes ✓' : 'No ✗'}`);
        console.log(`  Action: ${rule.action}`);
        console.log(`  Direction: ${rule.direction || 'N/A'}`);
        console.log(`  Protocol: ${rule.protocol || 'all'}`);
        console.log(`  Source IP: ${rule.srcIp || 'Any'}`);
        console.log(`  Source Port: ${rule.srcPort || 'Any'}`);
        console.log(`  Destination IP: ${rule.dstIp || 'Any'}`);
        console.log(`  Destination Port: ${rule.dstPort || 'Any'}`);
        console.log(`  Priority: ${rule.priority || 'N/A'}`);
        console.log('');

        if (rule.action === 'deny' || rule.action === 'reject') {
          console.log('  ⚠️  WARNING: This rule BLOCKS traffic!');
          console.log('');
        }
      });

      // Separate allow and deny rules
      const allowRules = relevantRules.filter((rule) => rule.action === 'allow');
      const denyRules = relevantRules.filter((rule) => rule.action === 'deny' || rule.action === 'reject');

      console.log('════════════════════════════════════════');
      console.log('Analysis');
      console.log('════════════════════════════════════════');
      console.log('');

      if (denyRules.length > 0 && allowRules.length === 0) {
        console.log('❌ Issue Found:');
        console.log('  Deny rules exist that block Blockscout, but no allow rules found.');
        console.log('  This explains the "No route to host" error.');
        console.log('');
      } else if (allowRules.length > 0 && denyRules.length > 0) {
        console.log('⚠️  Both allow and deny rules exist.');
        console.log('  Check rule priority - allow rules must be above deny rules.');
        console.log('');
      } else if (allowRules.length > 0) {
        console.log('✅ Allow rules exist for Blockscout.');
        console.log('  If issues persist, check rule priority or default policies.');
        console.log('');
      }
    } else {
      console.log('ℹ️  No firewall rules found that specifically target Blockscout.');
      console.log('');
      console.log('Checking all deny/reject rules...');
      console.log('');

      const denyRules = rules.filter(
        (rule) => rule.enable && (rule.action === 'deny' || rule.action === 'reject')
      );

      if (denyRules.length > 0) {
        console.log(`⚠️  Found ${denyRules.length} deny/reject rules:`);
        console.log('');
        denyRules.slice(0, 10).forEach((rule) => {
          console.log(`  - ${rule.name || rule.id} (Priority: ${rule.priority || 'N/A'})`);
          if (rule.dstIp) console.log(`    Dest: ${rule.dstIp}`);
          if (rule.dstPort) console.log(`    Port: ${rule.dstPort}`);
        });
        if (denyRules.length > 10) {
          console.log(`  ... and ${denyRules.length - 10} more`);
        }
        console.log('');
      }
    }

    console.log('════════════════════════════════════════');
    console.log('Recommendations');
    console.log('════════════════════════════════════════');
    console.log('');

    if (relevantRules.length === 0 || relevantRules.filter(r => r.action === 'allow').length === 0) {
      console.log('✅ Recommended Action:');
      console.log('  Create an allow rule in Omada Controller:');
      console.log('');
      console.log('  Name: Allow Internal to Blockscout HTTP');
      console.log('  Enable: Yes');
      console.log('  Action: Allow');
      console.log('  Direction: Forward');
      console.log('  Protocol: TCP');
      console.log('  Source IP: 192.168.11.0/24 (or leave blank for Any)');
      console.log('  Destination IP: 192.168.11.140');
      console.log('  Destination Port: 80');
      console.log('  Priority: High (ensure it\'s above any deny rules)');
      console.log('');
      console.log('  Then apply the configuration.');
      console.log('');
    }

    console.log('════════════════════════════════════════');
  } catch (error) {
    console.error('❌ Error:');
    console.error('');
    if (error.message) {
      console.error(`  ${error.message}`);
    } else {
      console.error('  ', error);
    }
    console.error('');
    process.exit(1);
  }
}

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

