#!/usr/bin/env tsx
/**
 * Pangolin API Endpoint Testing Script
 *
 * Tests all critical endpoints used by automated setup to verify Pangolin instance compatibility.
 * Run with: npx tsx api/test-pangolin-endpoints.ts
 */

import { env } from './src/config/env';
import { pangolinClient } from './src/services/pangolin.client';
import { logger } from './src/utils/logger';

interface TestResult {
  name: string;
  endpoint: string;
  status: 'pass' | 'fail' | 'warn' | 'skip';
  message: string;
  data?: unknown;
  error?: string;
}

const results: TestResult[] = [];

function logResult(result: TestResult) {
  const emoji = result.status === 'pass' ? '✅' : result.status === 'fail' ? '❌' : result.status === 'warn' ? '⚠️' : '⏭️';
  console.log(`${emoji} ${result.name}: ${result.message}`);
  if (result.error) {
    console.log(`   Error: ${result.error}`);
  }
  results.push(result);
}

async function testHealthCheck() {
  console.log('\n=== Testing Health & Configuration ===\n');

  // Test 1: Configuration check
  if (!pangolinClient.configured) {
    logResult({
      name: 'Configuration',
      endpoint: 'N/A',
      status: 'fail',
      message: 'Pangolin not configured',
      error: 'Missing required env vars: PANGOLIN_API_URL, PANGOLIN_API_KEY, or PANGOLIN_ORG_ID'
    });
    return false;
  }

  logResult({
    name: 'Configuration',
    endpoint: 'N/A',
    status: 'pass',
    message: `Configured with API URL: ${env.PANGOLIN_API_URL}, Org ID: ${env.PANGOLIN_ORG_ID}`
  });

  // Test 2: API health check
  try {
    const healthy = await pangolinClient.healthCheck();
    if (healthy) {
      logResult({
        name: 'API Health',
        endpoint: 'GET /',
        status: 'pass',
        message: 'API is reachable'
      });
      return true;
    } else {
      logResult({
        name: 'API Health',
        endpoint: 'GET /',
        status: 'fail',
        message: 'API returned non-200 status'
      });
      return false;
    }
  } catch (err) {
    logResult({
      name: 'API Health',
      endpoint: 'GET /',
      status: 'fail',
      message: 'Failed to connect to API',
      error: err instanceof Error ? err.message : String(err)
    });
    return false;
  }
}

async function testSiteEndpoints() {
  console.log('\n=== Testing Site Endpoints ===\n');

  // Test 1: List sites
  try {
    const sites = await pangolinClient.listSites();
    logResult({
      name: 'List Sites',
      endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/sites`,
      status: 'pass',
      message: `Found ${sites.length} sites`,
      data: sites.map(s => ({ siteId: s.siteId, name: s.name, subnet: s.subnet }))
    });

    // Test 2: Get site details (if sites exist)
    if (sites.length > 0 && sites[0]) {
      const siteId = sites[0].siteId;
      try {
        const site = await pangolinClient.getSite(siteId);
        logResult({
          name: 'Get Site Details',
          endpoint: `GET /site/${siteId}`,
          status: 'pass',
          message: `Retrieved site: ${site.name}`,
          data: { siteId: site.siteId, name: site.name, online: site.online, subnet: site.subnet }
        });
      } catch (err) {
        logResult({
          name: 'Get Site Details',
          endpoint: `GET /site/${siteId}`,
          status: 'fail',
          message: 'Failed to get site details',
          error: err instanceof Error ? err.message : String(err)
        });
      }
    } else {
      logResult({
        name: 'Get Site Details',
        endpoint: 'GET /site/{siteId}',
        status: 'skip',
        message: 'No sites available to test'
      });
    }
  } catch (err) {
    logResult({
      name: 'List Sites',
      endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/sites`,
      status: 'fail',
      message: 'Failed to list sites',
      error: err instanceof Error ? err.message : String(err)
    });
  }
}

async function testExitNodeEndpoints() {
  console.log('\n=== Testing Exit Node Endpoints ===\n');

  try {
    const exitNodes = await pangolinClient.listExitNodes();
    if (exitNodes.length > 0) {
      logResult({
        name: 'List Exit Nodes',
        endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/exit-nodes`,
        status: 'pass',
        message: `Found ${exitNodes.length} exit nodes`,
        data: exitNodes.map(n => ({
          exitNodeId: n.exitNodeId,
          name: n.name,
          online: n.online,
          location: n.location
        }))
      });
    } else {
      logResult({
        name: 'List Exit Nodes',
        endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/exit-nodes`,
        status: 'warn',
        message: 'No exit nodes available (self-hosted mode)',
        data: []
      });
    }
  } catch (err) {
    const errorMsg = err instanceof Error ? err.message : String(err);
    if (errorMsg.includes('404')) {
      logResult({
        name: 'List Exit Nodes',
        endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/exit-nodes`,
        status: 'warn',
        message: 'Endpoint not available (404) - self-hosted mode without separate exit nodes',
        error: 'This is expected for self-hosted Pangolin instances'
      });
    } else {
      logResult({
        name: 'List Exit Nodes',
        endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/exit-nodes`,
        status: 'fail',
        message: 'Failed to list exit nodes',
        error: errorMsg
      });
    }
  }
}

async function testDomainEndpoints() {
  console.log('\n=== Testing Domain Endpoints ===\n');

  try {
    const domains = await pangolinClient.listDomains();
    if (domains.length > 0) {
      logResult({
        name: 'List Domains',
        endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/domains`,
        status: 'pass',
        message: `Found ${domains.length} domains`,
        data: domains.map(d => ({
          domainId: d.domainId,
          baseDomain: d.baseDomain,
          verified: d.verified,
          type: d.type
        }))
      });

      // Check if DOMAIN env var matches any registered domain
      const envDomain = env.DOMAIN;
      const matchingDomain = domains.find(d => envDomain.endsWith(d.baseDomain));
      if (matchingDomain) {
        logResult({
          name: 'Domain Match',
          endpoint: 'N/A',
          status: 'pass',
          message: `Environment DOMAIN (${envDomain}) matches registered domain: ${matchingDomain.baseDomain}`,
          data: { domainId: matchingDomain.domainId, verified: matchingDomain.verified }
        });
      } else {
        logResult({
          name: 'Domain Match',
          endpoint: 'N/A',
          status: 'warn',
          message: `Environment DOMAIN (${envDomain}) does not match any registered domains`,
          error: `Available domains: ${domains.map(d => d.baseDomain).join(', ')}`
        });
      }
    } else {
      logResult({
        name: 'List Domains',
        endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/domains`,
        status: 'fail',
        message: 'No domains registered',
        error: 'You must register a domain in Pangolin dashboard before running automated setup'
      });
    }
  } catch (err) {
    logResult({
      name: 'List Domains',
      endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/domains`,
      status: 'fail',
      message: 'Failed to list domains',
      error: err instanceof Error ? err.message : String(err)
    });
  }
}

async function testResourceEndpoints() {
  console.log('\n=== Testing Resource Endpoints ===\n');

  try {
    const resources = await pangolinClient.listResources();
    logResult({
      name: 'List Site Resources',
      endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/site-resources`,
      status: 'pass',
      message: `Found ${resources.length} resources`,
      data: resources.slice(0, 5).map(r => ({
        resourceId: r.resourceId,
        name: r.name,
        fullDomain: r.fullDomain,
        ssl: r.ssl,
        active: r.active
      }))
    });

    // Test get resource details if any exist
    if (resources.length > 0 && resources[0]) {
      const resourceId = resources[0].resourceId;
      try {
        const resource = await pangolinClient.getResource(resourceId);
        logResult({
          name: 'Get Resource Details',
          endpoint: `GET /site-resource/${resourceId}`,
          status: 'pass',
          message: `Retrieved resource: ${resource.name}`,
          data: {
            resourceId: resource.resourceId,
            name: resource.name,
            fullDomain: resource.fullDomain,
            ssl: resource.ssl
          }
        });
      } catch (err) {
        logResult({
          name: 'Get Resource Details',
          endpoint: `GET /site-resource/${resourceId}`,
          status: 'fail',
          message: 'Failed to get resource details',
          error: err instanceof Error ? err.message : String(err)
        });
      }
    } else {
      logResult({
        name: 'Get Resource Details',
        endpoint: 'GET /site-resource/{resourceId}',
        status: 'skip',
        message: 'No resources available to test'
      });
    }
  } catch (err) {
    logResult({
      name: 'List Site Resources',
      endpoint: `GET /org/${env.PANGOLIN_ORG_ID}/site-resources`,
      status: 'fail',
      message: 'Failed to list resources',
      error: err instanceof Error ? err.message : String(err)
    });
  }
}

async function testCreateResourceFlow() {
  console.log('\n=== Testing Resource Creation Flow (Read-Only Validation) ===\n');

  // Validate we have the required data to create resources
  const domains = await pangolinClient.listDomains().catch(() => []);
  const sites = await pangolinClient.listSites().catch(() => []);

  if (domains.length === 0) {
    logResult({
      name: 'Resource Creation Pre-Check',
      endpoint: 'N/A',
      status: 'fail',
      message: 'Cannot create resources - no domains registered',
      error: 'Register a domain in Pangolin dashboard first'
    });
    return;
  }

  if (sites.length === 0) {
    logResult({
      name: 'Resource Creation Pre-Check',
      endpoint: 'N/A',
      status: 'warn',
      message: 'No sites exist - automated setup will create one',
      data: {
        note: 'First run of automated setup will create a new site',
        recommendedSubnet: '100.89.128.4/30'
      }
    });
  } else {
    logResult({
      name: 'Resource Creation Pre-Check',
      endpoint: 'N/A',
      status: 'pass',
      message: `Ready to create resources - ${sites.length} sites available`,
      data: sites.map(s => ({ siteId: s.siteId, name: s.name, subnet: s.subnet }))
    });
  }

  // Validate domain match
  const envDomain = env.DOMAIN;
  const matchingDomain = domains.find(d => envDomain.endsWith(d.baseDomain));

  if (matchingDomain) {
    logResult({
      name: 'Domain Validation',
      endpoint: 'N/A',
      status: 'pass',
      message: `Domain ${envDomain} is registered and ready`,
      data: { domainId: matchingDomain.domainId, baseDomain: matchingDomain.baseDomain }
    });
  } else {
    logResult({
      name: 'Domain Validation',
      endpoint: 'N/A',
      status: 'fail',
      message: `Domain ${envDomain} not found in Pangolin`,
      error: `Register ${envDomain} in Pangolin dashboard. Available: ${domains.map(d => d.baseDomain).join(', ')}`
    });
  }
}

function printSummary() {
  console.log('\n' + '='.repeat(60));
  console.log('TEST SUMMARY');
  console.log('='.repeat(60) + '\n');

  const passed = results.filter(r => r.status === 'pass').length;
  const failed = results.filter(r => r.status === 'fail').length;
  const warned = results.filter(r => r.status === 'warn').length;
  const skipped = results.filter(r => r.status === 'skip').length;

  console.log(`Total Tests: ${results.length}`);
  console.log(`✅ Passed: ${passed}`);
  console.log(`❌ Failed: ${failed}`);
  console.log(`⚠️  Warnings: ${warned}`);
  console.log(`⏭️  Skipped: ${skipped}\n`);

  if (failed > 0) {
    console.log('❌ CRITICAL FAILURES:\n');
    results.filter(r => r.status === 'fail').forEach(r => {
      console.log(`  - ${r.name} (${r.endpoint})`);
      console.log(`    ${r.message}`);
      if (r.error) console.log(`    Error: ${r.error}`);
      console.log('');
    });
  }

  if (warned > 0) {
    console.log('⚠️  WARNINGS:\n');
    results.filter(r => r.status === 'warn').forEach(r => {
      console.log(`  - ${r.name} (${r.endpoint})`);
      console.log(`    ${r.message}`);
      if (r.error) console.log(`    ${r.error}`);
      console.log('');
    });
  }

  // Overall verdict
  console.log('='.repeat(60));
  if (failed === 0) {
    console.log('✅ ALL CRITICAL TESTS PASSED');
    console.log('   Your Pangolin instance is ready for automated setup!');
  } else {
    console.log('❌ SOME TESTS FAILED');
    console.log('   Fix the critical failures above before running automated setup.');
  }
  console.log('='.repeat(60) + '\n');

  // Export detailed results to file
  const fs = require('fs');
  const path = require('path');
  const outputPath = path.join(process.cwd(), 'pangolin-test-results.json');
  fs.writeFileSync(outputPath, JSON.stringify({
    timestamp: new Date().toISOString(),
    summary: { total: results.length, passed, failed, warned, skipped },
    results
  }, null, 2));
  console.log(`📄 Detailed results saved to: ${outputPath}\n`);
}

async function main() {
  console.log('🔍 Pangolin API Endpoint Testing');
  console.log('='.repeat(60));
  console.log(`API URL: ${env.PANGOLIN_API_URL}`);
  console.log(`Org ID: ${env.PANGOLIN_ORG_ID}`);
  console.log(`Domain: ${env.DOMAIN}`);
  console.log('='.repeat(60));

  try {
    // Test 1: Health & Configuration
    const healthy = await testHealthCheck();
    if (!healthy) {
      console.log('\n❌ API health check failed - skipping remaining tests\n');
      printSummary();
      process.exit(1);
    }

    // Test 2: Site Endpoints
    await testSiteEndpoints();

    // Test 3: Exit Node Endpoints (optional)
    await testExitNodeEndpoints();

    // Test 4: Domain Endpoints
    await testDomainEndpoints();

    // Test 5: Resource Endpoints
    await testResourceEndpoints();

    // Test 6: Resource Creation Flow Validation
    await testCreateResourceFlow();

    // Print summary
    printSummary();

    // Exit with appropriate code
    const failed = results.filter(r => r.status === 'fail').length;
    process.exit(failed > 0 ? 1 : 0);
  } catch (err) {
    console.error('\n❌ Unexpected error during testing:', err);
    process.exit(1);
  }
}

main();
