#!/usr/bin/env node
/**
 * Check Omada firewall rules for Blockscout access
 * Queries Omada Controller API to list firewall rules that might block Blockscout
 */

import { OmadaClient } from '../omada-api/src/client/OmadaClient.js';
import { FirewallService } from '../omada-api/src/services/FirewallService.js';
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';

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

  // Get Omada credentials from environment
  const controllerUrl = process.env.OMADA_CONTROLLER_URL || process.env.OMADA_CONTROLLER_BASE_URL;
  const apiKey = process.env.OMADA_API_KEY || process.env.OMADA_CLIENT_ID;
  const apiSecret = process.env.OMADA_API_SECRET || process.env.OMADA_CLIENT_SECRET;
  const siteId = process.env.OMADA_SITE_ID;

  if (!controllerUrl || !apiKey || !apiSecret) {
    console.error('❌ Missing Omada credentials in .env file');
    console.error('');
    console.error('Required environment variables:');
    console.error('  OMADA_CONTROLLER_URL (or OMADA_CONTROLLER_BASE_URL)');
    console.error('  OMADA_API_KEY (or OMADA_CLIENT_ID)');
    console.error('  OMADA_API_SECRET (or OMADA_CLIENT_SECRET)');
    console.error('  OMADA_SITE_ID (optional)');
    process.exit(1);
  }

  console.log(`Controller URL: ${controllerUrl}`);
  console.log(`Site ID: ${siteId || 'auto-detect'}`);
  console.log('');

  try {
    // Initialize Omada client
    const client = new OmadaClient({
      baseUrl: controllerUrl,
      clientId: apiKey,
      clientSecret: apiSecret,
      siteId: siteId,
      verifySsl: process.env.OMADA_VERIFY_SSL !== 'false',
    });

    const firewallService = new FirewallService(client);

    console.log('Fetching firewall rules...');
    console.log('');

    // List all firewall rules
    const rules = await firewallService.listFirewallRules();

    console.log(`Found ${rules.length} firewall rules`);
    console.log('');

    // Filter rules that might affect Blockscout
    const relevantRules = rules.filter((rule) => {
      // Check if rule affects Blockscout IP or port 80
      const affectsBlockscoutIP = 
        !rule.dstIp || 
        rule.dstIp === BLOCKSCOUT_IP || 
        rule.dstIp.includes(BLOCKSCOUT_IP.split('.').slice(0, 3).join('.'));

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

      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('ℹ️  No firewall rules found that specifically target Blockscout');
      console.log('');
      console.log('Checking for default deny policies...');
      console.log('');

      // Check for default deny rules
      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 that might block traffic:`);
        console.log('');
        denyRules.forEach((rule) => {
          console.log(`  - ${rule.name} (Action: ${rule.action}, Priority: ${rule.priority})`);
        });
        console.log('');
      }

      // Check all rules for reference
      console.log('All firewall rules:');
      console.log('');
      rules.forEach((rule) => {
        const status = rule.enable ? '✓' : '✗';
        console.log(
          `  ${status} ${rule.name} (Action: ${rule.action}, Direction: ${rule.direction}, Priority: ${rule.priority})`
        );
      });
    } else {
      console.log(`🔍 Found ${relevantRules.length} rule(s) that might affect Blockscout:`);
      console.log('');

      relevantRules.forEach((rule) => {
        console.log(`Rule: ${rule.name}`);
        console.log(`  ID: ${rule.id}`);
        console.log(`  Enabled: ${rule.enable ? 'Yes' : 'No'}`);
        console.log(`  Action: ${rule.action}`);
        console.log(`  Direction: ${rule.direction}`);
        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}`);
        console.log('');

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

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

    // Check if there's an allow rule for Blockscout
    const allowRules = relevantRules.filter((rule) => rule.action === 'allow');
    const denyRules = relevantRules.filter(
      (rule) => rule.action === 'deny' || rule.action === 'reject'
    );

    if (denyRules.length > 0 && allowRules.length === 0) {
      console.log('❌ Issue Found:');
      console.log('  Deny rules exist that might block Blockscout, but no allow rules found.');
      console.log('');
      console.log('✅ Recommended Action:');
      console.log('  Create an allow rule with HIGH priority (above deny rules):');
      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 (above deny rules)');
      console.log('');
    } else if (allowRules.length > 0) {
      const highestAllowPriority = Math.max(...allowRules.map((r) => r.priority));
      const lowestDenyPriority = denyRules.length > 0 
        ? Math.min(...denyRules.map((r) => r.priority))
        : Infinity;

      if (highestAllowPriority < lowestDenyPriority) {
        console.log('✅ Configuration looks correct:');
        console.log('  Allow rules have higher priority than deny rules.');
        console.log('');
      } else {
        console.log('⚠️  Potential Issue:');
        console.log('  Some deny rules have higher priority than allow rules.');
        console.log('  Ensure allow rules are above deny rules in priority.');
        console.log('');
      }
    } else {
      console.log('ℹ️  No specific rules found for Blockscout.');
      console.log('  Traffic should be allowed by default (LAN → LAN on same subnet).');
      console.log('  If issues persist, check for default deny policies.');
      console.log('');
    }

    console.log('════════════════════════════════════════');
  } catch (error) {
    console.error('❌ Error querying Omada Controller:');
    console.error('');
    if (error.message) {
      console.error(`  ${error.message}`);
    } else {
      console.error('  ', error);
    }
    console.error('');
    console.error('Troubleshooting:');
    console.error('  1. Verify Omada Controller is accessible');
    console.error('  2. Check API credentials in .env file');
    console.error('  3. Verify network connectivity to controller');
    process.exit(1);
  }
}

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

