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

const PORT = 3009;
const BASE_URL = `http://localhost:${PORT}`;

let nextProcess;
let prisma;
let pool;

function getPrisma() {
  if (!prisma) {
    const dbUrl = new URL(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;
}

// Helper to run query with admin context
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));
}

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 runTests() {
  console.log("=== STARTING PHASE 2 AUTH & RLS INTEGRATION TEST SUITE ===");

  const db = getPrisma();

  // 1. Seed test sales data as Admin for the test queries
  console.log("\nSeeding test sales results...");
  const users = await runAsAdmin(tx => tx.user.findMany());
  const colaboradorMde = users.find(u => u.username === 'colaborador_mde');
  const gerenteMde = users.find(u => u.username === 'gerente_mde');
  const liderCtg = users.find(u => u.username === 'lider_ctg');
  const adminUser = users.find(u => u.username === 'admin');

  await runAsAdmin(async (tx) => {
    await tx.salesResult.deleteMany({
      where: {
        idempotencyKey: { in: ['auth-test-colab', 'auth-test-lider'] }
      }
    });

    // Colaborador sale
    await tx.salesResult.create({
      data: {
        source: 'EXCEL',
        hotelId: colaboradorMde.hotelId,
        userId: colaboradorMde.id,
        period: '2026-06',
        amount: 3000.00,
        salesCount: 5,
        idempotencyKey: 'auth-test-colab',
        uploadedBy: adminUser.id
      }
    });

    // Lider sale
    await tx.salesResult.create({
      data: {
        source: 'EXCEL',
        hotelId: liderCtg.hotelId,
        userId: liderCtg.id,
        period: '2026-06',
        amount: 6000.00,
        salesCount: 8,
        idempotencyKey: 'auth-test-lider',
        uploadedBy: adminUser.id
      }
    });
  });
  // 2. Start Next.js server
  console.log(`\nStarting Next.js dev server on port ${PORT}...`);
  nextProcess = spawn('node', ['node_modules/next/dist/bin/next', 'dev', '--port', String(PORT)], {
    env: { ...process.env, PORT: String(PORT) }
  });

  nextProcess.stdout.on('data', (data) => console.log(`[Next.js] ${data.toString().trim()}`));
  nextProcess.stderr.on('data', (data) => console.error(`[Next.js ERR] ${data.toString().trim()}`));

  // Wait for server to start up
  let serverReady = false;
  for (let i = 0; i < 15; i++) {
    await sleep(2000);
    try {
      await fetch(`${BASE_URL}/api/auth/me`);
      serverReady = true;
      break;
    } catch (e) {
      // Server not ready yet
    }
  }

  if (!serverReady) {
    console.error("Error: Next.js dev server failed to start within timeout.");
    cleanup();
    process.exit(1);
  }
  console.log("Next.js dev server is ready!");

  // --- TEST A: Unauthenticated me query ---
  console.log("\nRunning TEST A: Unauthenticated /api/auth/me...");
  try {
    const res = await fetch(`${BASE_URL}/api/auth/me`);
    assert(res.status === 401, "GET /api/auth/me returns 401 Unauthorized when no cookie is set");
  } catch (err) {
    console.error("Test A error:", err);
    failed++;
  }

  // --- TEST B: Authentication with Invalid Credentials ---
  console.log("\nRunning TEST B: Authentication with Invalid Credentials...");
  try {
    const res = await fetch(`${BASE_URL}/api/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: 'colaborador_mde', password: 'wrongpassword' })
    });
    assert(res.status === 401, "POST /api/auth/login returns 401 for incorrect password");
  } catch (err) {
    console.error("Test B error:", err);
    failed++;
  }

  // --- TEST C: Authenticate Colaborador and verify RLS ---
  console.log("\nRunning TEST C: Authenticate Colaborador & Verify RLS...");
  try {
    // 1. Login
    const loginRes = await fetch(`${BASE_URL}/api/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: 'colaborador_mde', password: 'password123' })
    });
    if (loginRes.status !== 200) {
      console.error("Login failed response:", loginRes.status, await loginRes.text());
    }
    assert(loginRes.status === 200, "POST /api/auth/login returns 200 OK for valid Colaborador credentials");
    
    // Save session cookie
    const setCookie = loginRes.headers.get('set-cookie');
    assert(setCookie !== null && setCookie.includes('session='), "Response contains 'session' cookie");
    const cookie = setCookie ? setCookie.split(';')[0] : '';

    // 2. Query /api/auth/me
    const meRes = await fetch(`${BASE_URL}/api/auth/me`, {
      headers: { Cookie: cookie }
    });
    const meData = await meRes.json();
    if (meRes.status !== 200) {
      console.error("GET /api/auth/me failed status:", meRes.status, "body:", meData);
    }
    assert(
      meRes.status === 200 && meData.user && meData.user.username === 'colaborador_mde',
      "GET /api/auth/me returns user details for active session"
    );

    // 3. Query /api/test-auth (verify RLS)
    const testAuthRes = await fetch(`${BASE_URL}/api/test-auth`, {
      headers: { Cookie: cookie }
    });
    const testAuthData = await testAuthRes.json();

    assert(
      testAuthData.users.length === 1 && testAuthData.users[0].username === 'colaborador_mde',
      "RLS restriction: Colaborador only sees their own user record in queries"
    );
    assert(
      testAuthData.sales.length === 1 && testAuthData.sales[0].userId === colaboradorMde.id,
      "RLS restriction: Colaborador only sees their own sales results in queries"
    );
  } catch (err) {
    console.error("Test C error:", err);
    failed++;
  }

  // --- TEST D: Authenticate Gerente and verify RLS ---
  console.log("\nRunning TEST D: Authenticate Gerente & Verify RLS...");
  try {
    // 1. Login
    const loginRes = await fetch(`${BASE_URL}/api/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: 'gerente_mde', password: 'password123' })
    });
    const setCookie = loginRes.headers.get('set-cookie');
    const cookie = setCookie ? setCookie.split(';')[0] : '';

    // 2. Query /api/test-auth (verify RLS)
    const testAuthRes = await fetch(`${BASE_URL}/api/test-auth`, {
      headers: { Cookie: cookie }
    });
    const testAuthData = await testAuthRes.json();

    const mdeUsers = users.filter(u => u.hotelId === gerenteMde.hotelId);
    const visibleToGerente = testAuthData.users.every(u => u.hotelId === gerenteMde.hotelId);
    
    assert(
      testAuthData.users.length === mdeUsers.length && visibleToGerente,
      `RLS restriction: Gerente only sees users within their hotel (EST-MDE, count: ${mdeUsers.length})`
    );
    
    const visibleSales = testAuthData.sales.every(s => s.hotelId === gerenteMde.hotelId);
    assert(
      testAuthData.sales.length === 1 && visibleSales,
      "RLS restriction: Gerente only sees sales results within their hotel"
    );
  } catch (err) {
    console.error("Test D error:", err);
    failed++;
  }

  // --- TEST E: Authenticate Admin and verify RLS bypass ---
  console.log("\nRunning TEST E: Authenticate Admin & Verify RLS Bypass...");
  try {
    // 1. Login
    const loginRes = await fetch(`${BASE_URL}/api/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username: 'admin', password: 'password123' })
    });
    const setCookie = loginRes.headers.get('set-cookie');
    const cookie = setCookie ? setCookie.split(';')[0] : '';

    // 2. Query /api/test-auth (verify RLS)
    const testAuthRes = await fetch(`${BASE_URL}/api/test-auth`, {
      headers: { Cookie: cookie }
    });
    const testAuthData = await testAuthRes.json();

    assert(
      testAuthData.users.length === users.length,
      "RLS bypass: Admin sees all users in the system"
    );
    assert(
      testAuthData.sales.length === 2,
      "RLS bypass: Admin sees all sales results in the system"
    );
  } catch (err) {
    console.error("Test E error:", err);
    failed++;
  }

  await cleanup();

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

async function cleanup() {
  console.log("\nCleaning up test records and shutting down servers...");
  try {
    if (prisma) {
      await runAsAdmin(async (tx) => {
        await tx.salesResult.deleteMany({
          where: {
            idempotencyKey: { in: ['auth-test-colab', 'auth-test-lider'] }
          }
        });
      });
      await prisma.$disconnect();
    }
    if (pool) {
      await pool.end();
    }
  } catch (e) {
    console.error("Error during DB cleanup:", e);
  }

  if (nextProcess) {
    nextProcess.kill();
  }
}

// Handle exit gracefully
process.on('SIGINT', () => {
  cleanup();
  process.exit(1);
});

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