const fs = require('fs');
const path = require('path');
const { PrismaClient } = require('@prisma/client');
const { PrismaPg } = require('@prisma/adapter-pg');
const { Pool } = require('pg');
require('dotenv').config();

// Base configurations - Target the dev server directly via localhost port
const BASE_URL = "http://127.0.0.1:3001";

let prisma;
let pool;
let workflowId;
let n8nUrl;
let n8nApiKey;

// Helper to run query with admin credentials
function getPrisma() {
  if (!prisma) {
    const dbUrl = new URL(process.env.DATABASE_URL_DEV || process.env.DATABASE_URL);
    pool = new Pool({
      host: dbUrl.hostname,
      port: dbUrl.port ? parseInt(dbUrl.port) : 5432,
      user: decodeURIComponent(dbUrl.username),
      password: decodeURIComponent(dbUrl.password),
      database: dbUrl.pathname.substring(1).split('?')[0],
      ssl: false
    });
    const adapter = new PrismaPg(pool);
    prisma = new PrismaClient({ adapter });
  }
  return prisma;
}

async function runAsAdmin(queryFn) {
  const db = getPrisma();
  return db.$transaction(async (tx) => {
    await tx.$executeRawUnsafe(`SET LOCAL app.current_user_role = 'admin';`);
    return queryFn(tx);
  });
}

async function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Custom simple assertion
let passed = 0;
let failed = 0;
function assert(condition, message) {
  if (condition) {
    console.log(`  ✓ PASS: ${message}`);
    passed++;
  } else {
    console.error(`  ✗ FAIL: ${message}`);
    failed++;
  }
}

async function cleanupDb() {
  console.log("Cleaning up integration test records in test database...");
  try {
    await runAsAdmin(async (tx) => {
      console.log("DEBUG: Available models on tx:", Object.keys(tx).filter(k => !k.startsWith('$')));
      // Clean sales results
      await tx.salesResult.deleteMany({
        where: {
          idempotencyKey: { startsWith: 'key-integration' }
        }
      });
      // Clean import jobs
      if (tx.salesImportJob) {
        await tx.salesImportJob.deleteMany({
          where: {
            idempotencyKey: { startsWith: 'key-integration' }
          }
        });
      } else {
        console.warn("WARNING: tx.salesImportJob is undefined, skipping deleteMany.");
      }
      // Clean audit logs
      await tx.auditLog.deleteMany({
        where: {
          action: 'CREATE',
          targetTable: 'sales_results'
        }
      });
    });
  } catch (err) {
    console.error("Error during DB cleanup:", err);
  }
}

async function runTests() {
  console.log("=== STARTING REAL N8N E2E INTEGRATION TEST SUITE ===");

  // 1. Clean DB
  await cleanupDb();

  // 2. Fetch n8n API configuration
  const mcpConfigPath = path.join(__dirname, '../.agents/mcp_config.json');
  if (!fs.existsSync(mcpConfigPath)) {
    console.error("Error: .agents/mcp_config.json not found.");
    process.exit(1);
  }
  
  const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
  const n8nEnv = mcpConfig.mcpServers.n8n.env;
  n8nUrl = n8nEnv.N8N_API_URL || "https://n8n.gaboggamer.online";
  n8nApiKey = n8nEnv.N8N_API_KEY;

  if (!n8nApiKey) {
    console.error("Error: N8N_API_KEY is missing in mcp_config.json.");
    process.exit(1);
  }

  // 3. Load and modify n8n workflow JSON to point to our local test server via Docker bridge
  const workflowPath = path.join(__dirname, '../n8n/sales_import_workflow.json');
  if (!fs.existsSync(workflowPath)) {
    console.error("Error: n8n/sales_import_workflow.json not found.");
    process.exit(1);
  }

  const workflowJson = JSON.parse(fs.readFileSync(workflowPath, 'utf8'));
  const webhookPath = `calculate-commissions-${Date.now()}`;

  // Point callbacks to dev server, set dev test signature, and randomize path to prevent webhook conflicts
  for (const node of workflowJson.nodes) {
    if (node.id === 'Set-Dev-Env' || node.id === 'Set-Prod-Env') {
      node.parameters.values.string = [
        { name: 'appUrl', value: 'http://special-hotel-dev:3000' },
        { name: 'signature', value: process.env.N8N_WEBHOOK_SECRET_DEV || 'qUHuPqPjA65psdtwQU7zgp/DVkvd1xXk2WP/vzbnEdc=' }
      ];
    }
    if (node.type === 'n8n-nodes-base.webhook') {
      node.parameters.path = webhookPath;
    }
  }

  // 3.5. Fetch existing workflows to retrieve the project ID dynamically
  console.log("Fetching project ID from existing workflows...");
  let projectId;
  try {
    const listRes = await fetch(`${n8nUrl}/api/v1/workflows`, {
      method: 'GET',
      headers: {
        'X-N8N-API-KEY': n8nApiKey
      }
    });
    if (listRes.ok) {
      const listData = await listRes.json();
      const firstWf = listData.data.find(w => w.shared && w.shared.length > 0);
      if (firstWf) {
        projectId = firstWf.shared[0].projectId;
        console.log(`Resolved project ID dynamically: ${projectId}`);
      }
    }
  } catch (err) {
    console.warn("Warning: Could not fetch project ID dynamically, falling back to default.", err.message);
  }

  // 3.6. Fetch credentials dynamically to resolve AI accounts
  console.log("Fetching credentials from n8n to resolve AI accounts...");
  let deepSeekCred = null;
  let geminiCred = null;
  try {
    const credsRes = await fetch(`${n8nUrl}/api/v1/credentials`, {
      method: 'GET',
      headers: {
        'X-N8N-API-KEY': n8nApiKey
      }
    });
    if (credsRes.ok) {
      const credsData = await credsRes.json();
      const credentials = Array.isArray(credsData.data) ? credsData.data : (credsData.data.credentials || []);
      
      const deepSeekCreds = credentials.filter(c => c.type === 'deepSeekApi');
      if (deepSeekCreds.length > 0) {
        deepSeekCred = deepSeekCreds.find(c => c.name.toLowerCase().includes('deepseek')) || deepSeekCreds[0];
      }
      
      const geminiCreds = credentials.filter(c => c.type === 'googlePalmApi');
      if (geminiCreds.length > 0) {
        geminiCred = geminiCreds.find(c => c.name.toLowerCase().includes('gemini') || c.name.toLowerCase().includes('google')) || geminiCreds[0];
      }
      
      if (deepSeekCred) {
        console.log(`Resolved DeepSeek credential: "${deepSeekCred.name}" (ID: ${deepSeekCred.id})`);
      }
      if (geminiCred) {
        console.log(`Resolved Gemini credential: "${geminiCred.name}" (ID: ${geminiCred.id})`);
      }
    } else {
      console.warn(`Warning: Failed to fetch credentials. Status: ${credsRes.status}`);
    }
  } catch (err) {
    console.warn("Warning: Could not fetch credentials dynamically:", err.message);
  }

  // Inject resolved credentials dynamically into the workflow JSON
  for (const node of workflowJson.nodes) {
    if (node.type === '@n8n/n8n-nodes-langchain.lmChatDeepSeek' && deepSeekCred) {
      node.credentials = {
        deepSeekApi: {
          id: deepSeekCred.id,
          name: deepSeekCred.name
        }
      };
      console.log(`Injected DeepSeek credential into node "${node.name}"`);
    }
    if (node.type === '@n8n/n8n-nodes-langchain.lmChatGoogleGemini' && geminiCred) {
      node.credentials = {
        googlePalmApi: {
          id: geminiCred.id,
          name: geminiCred.name
        }
      };
      console.log(`Injected Gemini credential into node "${node.name}"`);
    }
  }

  // 4. Create workflow in n8n
  console.log("Deploying workflow to real n8n instance...");
  const workflowName = `Semillero E2E Integration: Sales Import - ${Date.now()}`;
  const createRes = await fetch(`${n8nUrl}/api/v1/workflows`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-N8N-API-KEY': n8nApiKey
    },
    body: JSON.stringify({
      name: workflowName,
      nodes: workflowJson.nodes,
      connections: workflowJson.connections,
      settings: workflowJson.settings || {},
      projectId
    })
  });

  if (!createRes.ok) {
    const errorText = await createRes.text();
    console.error(`Error: Failed to create n8n workflow. Status: ${createRes.status}. Output: ${errorText}`);
    process.exit(1);
  }

  const createData = await createRes.json();
  workflowId = createData.id;
  console.log(`Workflow deployed successfully! ID: ${workflowId}. Full response:`, JSON.stringify(createData));

  // Activate the workflow if it is inactive (normally setting active in POST is enough, but double-checking)
  if (!createData.active) {
    console.log("Activating workflow...");
    const activateRes = await fetch(`${n8nUrl}/api/v1/workflows/${workflowId}/activate`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-N8N-API-KEY': n8nApiKey
      },
      body: JSON.stringify({})
    });
    if (!activateRes.ok) {
      console.error("Failed to activate workflow:", await activateRes.text());
    } else {
      console.log("Workflow activated successfully! Waiting 5 seconds for n8n webhooks to register...");
      await sleep(5000);
    }
  }

  // 5. Verify the external dev server is running
  console.log(`Verifying target dev server at ${BASE_URL}...`);
  try {
    const res = await fetch(`${BASE_URL}/api/auth/me`);
    if (!res.ok) {
      console.warn(`Warning: Target server returned status ${res.status}`);
    }
  } catch (e) {
    console.error(`Error: Dev server is unreachable at ${BASE_URL}. Error:`, e.message);
    await cleanup();
    process.exit(1);
  }

  // 6. Generate test parameters
  const idempotencyKey = `key-integration-${Date.now()}`;

  // 7. Trigger the n8n webhook directly
  console.log(`Triggering n8n webhook directly at ${n8nUrl}/webhook/${webhookPath}...`);
  const uploadRes = await fetch(`${n8nUrl}/webhook/${webhookPath}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-n8n-signature': process.env.N8N_WEBHOOK_SECRET_DEV || 'qUHuPqPjA65psdtwQU7zgp/DVkvd1xXk2WP/vzbnEdc='
    },
    body: JSON.stringify({
      idempotencyKey,
      uploaderId: 1,
      sales: [
        {
          username: 'colaborador_mde',
          hotelCode: 'EST-MDE',
          period: '2026-06',
          amount: 15000,
          salesCount: 5,
          transactionId: 'TX-EST-MDE-001'
        }
      ]
    })
  });

  assert(uploadRes.status === 200 || uploadRes.status === 202, `n8n webhook response code is ${uploadRes.status} (expected 200/202)`);
  const uploadData = await uploadRes.json();
  assert(uploadData.success && uploadData.code === 'IMPORT_ACCEPTED', "n8n accepted the webhook trigger");

  // 8. Poll DB for record insert from n8n callback
  console.log("Waiting for n8n to complete processing and send callback to Next.js...");
  let callbackCompleted = false;
  for (let i = 0; i < 20; i++) {
    await sleep(2000);
    const dbSales = await runAsAdmin(tx => tx.salesResult.findMany({
      where: {
        idempotencyKey: {
          startsWith: idempotencyKey
        }
      }
    }));
    
    if (dbSales.length > 0) {
      assert(dbSales.length === 1, "1 sales result record successfully processed and saved via n8n integration!");
      assert(parseFloat(dbSales[0].amount) === 15000.00, "Sales amount verified (15,000.00)");
      callbackCompleted = true;
      break;
    }
  }

  assert(callbackCompleted, "n8n background execution and database write callback verified");

  await cleanup();

  console.log(`\n=== REAL N8N INTEGRATION TEST RESULTS: ${passed} PASSED, ${failed} FAILED ===`);
  if (failed > 0) {
    process.exit(1);
  } else {
    process.exit(0);
  }
}

async function cleanup() {
  console.log("\nCleaning up integration test processes...");
  
  // 1. Delete workflow from n8n
  if (workflowId) {
    if (failed > 0) {
      console.log(`[TEST FAILED] Skipping test workflow ${workflowId} deletion to allow troubleshooting in n8n.gaboggamer.online.`);
    } else {
      console.log(`Deleting n8n test workflow ${workflowId}...`);
      try {
        const delRes = await fetch(`${n8nUrl}/api/v1/workflows/${workflowId}`, {
          method: 'DELETE',
          headers: {
            'X-N8N-API-KEY': n8nApiKey
          }
        });
        if (delRes.ok) {
          console.log("Test workflow deleted successfully from n8n.");
        } else {
          console.error("Failed to delete test workflow:", await delRes.text());
        }
      } catch (e) {
        console.error("Error deleting workflow:", e);
      }
    }
  }

  // 3. Clean DB records
  await cleanupDb();

  // 4. Disconnect Prisma
  try {
    if (prisma) {
      await prisma.$disconnect();
    }
    if (pool) {
      await pool.end();
    }
  } catch (e) {}
}

process.on('SIGINT', async () => {
  await cleanup();
  process.exit(1);
});

runTests().catch(async err => {
  console.error("Fatal E2E integration test runner error:", err);
  await cleanup();
  process.exit(1);
});
