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

const dbUrl = new URL(process.env.DATABASE_URL);
const 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);
const prisma = new PrismaClient({ adapter });

async function runTests() {
  console.log("=== STARTING RLS AND SEED TEST SUITE ===");

  // 1. Fetch seeded data to map IDs (Run as ADMIN to bypass RLS)
  const { users, hotels, regions } = await prisma.$transaction(async (tx) => {
    await tx.$executeRawUnsafe(`SET LOCAL app.current_user_role = 'admin';`);
    const users = await tx.user.findMany({ include: { hotel: true } });
    const hotels = await tx.hotel.findMany();
    const regions = await tx.region.findMany();
    return { users, hotels, regions };
  });

  const adminUser = users.find(u => u.username === 'admin');
  const gerenteMde = users.find(u => u.username === 'gerente_mde');
  const liderCtg = users.find(u => u.username === 'lider_ctg');
  const colaboradorMde = users.find(u => u.username === 'colaborador_mde');

  if (!adminUser || !gerenteMde || !liderCtg || !colaboradorMde) {
    console.error("Error: Could not find all required seeded users. Run database seeding first.");
    process.exit(1);
  }

  console.log(`Seeded users found:
- Admin ID: ${adminUser.id}
- Gerente MDE ID: ${gerenteMde.id} (Hotel ID: ${gerenteMde.hotelId})
- Lider CTG ID: ${liderCtg.id} (Hotel ID: ${liderCtg.hotelId})
- Colaborador MDE ID: ${colaboradorMde.id} (Hotel ID: ${colaboradorMde.hotelId})`);

  let passed = 0;
  let failed = 0;

  function assert(condition, message) {
    if (condition) {
      console.log(`  ✓ PASS: ${message}`);
      passed++;
    } else {
      console.error(`  ✗ FAIL: ${message}`);
      failed++;
    }
  }

  // Helper to run query with context
  async function runWithContext(user, queryFn) {
    return prisma.$transaction(async (tx) => {
      if (user) {
        await tx.$executeRawUnsafe(`SET LOCAL app.current_user_id = '${user.id}';`);
        await tx.$executeRawUnsafe(`SET LOCAL app.current_user_role = '${user.role}';`);
        await tx.$executeRawUnsafe(`SET LOCAL app.current_hotel_id = '${user.hotelId}';`);
        await tx.$executeRawUnsafe(`SET LOCAL app.current_region_id = '${user.hotel.regionId}';`);
      } else {
        // Clear context
        await tx.$executeRawUnsafe(`RESET ALL;`);
      }
      return queryFn(tx);
    });
  }

  // --- TEST 1: User Table Read Restrictions ---
  console.log("\nRunning TEST 1: User Table Read Restrictions...");
  try {
    // Colaborador should only see their own user record
    const colabUsers = await runWithContext(colaboradorMde, tx => tx.user.findMany());
    assert(
      colabUsers.length === 1 && colabUsers[0].id === colaboradorMde.id,
      "Colaborador can only select their own user record"
    );

    // Gerente should see all users in their hotel
    const gerenteUsers = await runWithContext(gerenteMde, tx => tx.user.findMany());
    const allInMdeHotel = users.filter(u => u.hotelId === gerenteMde.hotelId);
    const visibleToGerenteInMde = gerenteUsers.every(u => u.hotelId === gerenteMde.hotelId);
    assert(
      gerenteUsers.length === allInMdeHotel.length && visibleToGerenteInMde,
      `Gerente MDE sees exactly ${allInMdeHotel.length} users in their hotel and no others`
    );

    // Admin should see all users
    const adminUsers = await runWithContext(adminUser, tx => tx.user.findMany());
    assert(
      adminUsers.length === users.length,
      "Admin sees all users in the system"
    );
  } catch (err) {
    console.error("Error in TEST 1:", err);
    failed++;
  }

  // --- TEST 2: Sales Results RLS ---
  console.log("\nRunning TEST 2: Sales Results RLS...");
  let saleColab, saleOther;
  try {
    // Clean up test sales results first (non-destructively)
    await runWithContext(adminUser, tx => tx.salesResult.deleteMany({
      where: { idempotencyKey: { in: ['test-key-colab-1', 'test-key-other-1'] } }
    }));

    // Create sales results as Admin (bypass RLS)
    saleColab = await runWithContext(adminUser, tx => tx.salesResult.create({
      data: {
        source: 'EXCEL',
        hotelId: colaboradorMde.hotelId,
        userId: colaboradorMde.id,
        period: '2026-06',
        amount: 5000.00,
        salesCount: 10,
        idempotencyKey: 'test-key-colab-1',
        uploadedBy: adminUser.id
      }
    }));

    saleOther = await runWithContext(adminUser, tx => tx.salesResult.create({
      data: {
        source: 'EXCEL',
        hotelId: liderCtg.hotelId,
        userId: liderCtg.id,
        period: '2026-06',
        amount: 8000.00,
        salesCount: 15,
        idempotencyKey: 'test-key-other-1',
        uploadedBy: adminUser.id
      }
    }));

    // Colaborador queries sales results
    const colabSales = await runWithContext(colaboradorMde, tx => tx.salesResult.findMany());
    assert(
      colabSales.length === 1 && colabSales[0].id === saleColab.id,
      "Colaborador can only select their own sales results"
    );

    // Gerente queries sales results
    const gerenteSales = await runWithContext(gerenteMde, tx => tx.salesResult.findMany());
    assert(
      gerenteSales.length === 1 && gerenteSales[0].id === saleColab.id,
      "Gerente can only select sales results for their hotel"
    );

    // Admin queries sales results
    const adminSales = await runWithContext(adminUser, tx => tx.salesResult.findMany());
    assert(
      adminSales.length === 2,
      "Admin can select all sales results"
    );
  } catch (err) {
    console.error("Error in TEST 2:", err);
    failed++;
  }

  // --- TEST 3: Audit Log Immutability ---
  console.log("\nRunning TEST 3: Audit Log Immutability...");
  try {
    // Create audit log entry
    const log = await runWithContext(adminUser, tx => tx.auditLog.create({
      data: {
        userId: adminUser.id,
        action: 'CREATE',
        targetTable: 'compensation_plans',
        targetId: 1,
        ipAddress: '127.0.0.1'
      }
    }));

    // Try to update audit log as Admin
    let updateFailed = false;
    try {
      await runWithContext(adminUser, tx => tx.auditLog.update({
        where: { id: log.id },
        data: { ipAddress: '8.8.8.8' }
      }));
    } catch (e) {
      updateFailed = true;
    }
    assert(updateFailed, "AuditLog update is blocked by RLS even for admin");

    // Try to delete audit log as Admin
    let deleteFailed = false;
    try {
      await runWithContext(adminUser, tx => tx.auditLog.delete({
        where: { id: log.id }
      }));
    } catch (e) {
      deleteFailed = true;
    }
    assert(deleteFailed, "AuditLog delete is blocked by RLS even for admin");

  } catch (err) {
    console.error("Error in TEST 3:", err);
    failed++;
  }

  // Clean up test sales results
  console.log("\nCleaning up test records...");
  const salesResultIds = [];
  if (saleColab) salesResultIds.push(saleColab.id);
  if (saleOther) salesResultIds.push(saleOther.id);
  if (salesResultIds.length > 0) {
    await runWithContext(adminUser, tx => tx.salesResult.deleteMany({
      where: { id: { in: salesResultIds } }
    }));
  }
  // Audit logs are not deleted due to write-only RLS immutability

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

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