const assert = require('assert');
const axios = require('axios');

const api = axios.create({
  baseURL: 'http://127.0.0.1:3000/api',
  validateStatus: () => true,
});

const SUPER_ADMIN = {
  email: 'super_admin@flatlogic.com',
  password: '252a3d31',
};

function decodeUserId(token) {
  const payload = token.split('.')[1];
  return JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')).user.id;
}

async function signIn(credentials) {
  const response = await api.post('/auth/signin/local', credentials);
  assert.strictEqual(response.status, 200, `Expected signin to succeed for ${credentials.email}, got ${response.status}: ${response.data}`);
  return response.data;
}

async function sql(token, sqlQuery) {
  const response = await api.post(
    '/sql',
    { sql: sqlQuery },
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    },
  );

  assert.strictEqual(response.status, 200, `Expected SQL helper to succeed, got ${response.status}: ${JSON.stringify(response.data)}`);
  return response.data.rows;
}

async function createOrganization(token, name) {
  const response = await api.post(
    '/organizations',
    { data: { name } },
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    },
  );

  assert.strictEqual(response.status, 200, `Expected organization creation to succeed, got ${response.status}: ${response.data}`);

  const rows = await sql(token, `select id from organizations where name = '${name.replace(/'/g, "''")}' order by "createdAt" desc limit 1`);
  assert.ok(rows[0]?.id, 'Expected organization lookup to return an id');
  return rows[0].id;
}

async function createTenant(token, { name, subdomain, organizationsId }) {
  const response = await api.post(
    '/tenants',
    {
      data: {
        name,
        subdomain,
        organizations: organizationsId,
        default_language: 'fr',
        primary_currency: 'USD',
      },
    },
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    },
  );

  assert.strictEqual(response.status, 200, `Expected tenant creation to succeed, got ${response.status}: ${response.data}`);
}

async function promoteUser(token, { userId, email, organizationsId, roleId }) {
  const response = await api.put(
    `/users/${userId}`,
    {
      id: userId,
      data: {
        email,
        firstName: email.split('@')[0],
        emailVerified: true,
        app_role: roleId,
        organizations: organizationsId,
      },
    },
    {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    },
  );

  assert.strictEqual(response.status, 200, `Expected user promotion to succeed, got ${response.status}: ${response.data}`);
}

async function signupTenantUser({ subdomain, organizationsId, email, roleId, superAdminToken }) {
  const signupResponse = await api.post(
    '/auth/signup',
    {
      email,
      password: 'demo1234',
      organizationsId,
    },
    {
      headers: {
        Host: `${subdomain}.example.com`,
        Referer: `http://${subdomain}.example.com/register`,
      },
    },
  );

  assert.strictEqual(signupResponse.status, 200, `Expected tenant signup to succeed, got ${signupResponse.status}: ${signupResponse.data}`);

  const userId = decodeUserId(signupResponse.data);
  await promoteUser(superAdminToken, {
    userId,
    email,
    organizationsId,
    roleId,
  });

  const rows = await sql(superAdminToken, `select "organizationsId" from users where id = '${userId}'`);
  assert.strictEqual(rows[0].organizationsId, organizationsId, 'Expected signup to persist the resolved organizationsId');

  return signIn({ email, password: 'demo1234' });
}

function buildContactPayload(suffix) {
  return {
    full_name: 'Jean Test',
    company_name: `Company ${suffix}`,
    email: `lead-${suffix}@company.com`,
    phone: '+243800000000',
    service_line: 'cloud',
    company_size: '11_50',
    budget_range: '20_50k',
    preferred_channel: 'email',
    urgency: 'urgent',
    message: 'Nous avons besoin d\'une migration cloud sécurisée et bien cadrée.',
  };
}

describe('tenant isolation and tenant-scoped public intake', function () {
  this.timeout(120000);

  it('rejects tenant discovery and contact intake when no valid tenant context is present', async () => {
    const orgResponse = await api.get('/org-for-auth');
    assert.strictEqual(orgResponse.status, 400);
    assert.match(String(orgResponse.data), /tenant subdomain/i);

    const intakeResponse = await api.post('/contact-form', buildContactPayload('unknown'), {
      headers: {
        Host: 'unknown.example.com',
        Referer: 'http://unknown.example.com/',
      },
    });
    assert.strictEqual(intakeResponse.status, 400);
    assert.match(String(intakeResponse.data), /No tenant is configured/i);
  });

  it('prevents cross-organization read, update, delete, and direct SQL access by id', async () => {
    const superAdminToken = await signIn(SUPER_ADMIN);
    const timestamp = Date.now();
    const adminRoleRows = await sql(superAdminToken, "select id from roles where name = 'Administrator' limit 1");
    const adminRoleId = adminRoleRows[0].id;

    const orgA = await createOrganization(superAdminToken, `Isolation Org A ${timestamp}`);
    const orgB = await createOrganization(superAdminToken, `Isolation Org B ${timestamp}`);
    const subA = `isolation-a-${timestamp}`;
    const subB = `isolation-b-${timestamp}`;

    await createTenant(superAdminToken, {
      name: `Isolation Tenant A ${timestamp}`,
      subdomain: subA,
      organizationsId: orgA,
    });
    await createTenant(superAdminToken, {
      name: `Isolation Tenant B ${timestamp}`,
      subdomain: subB,
      organizationsId: orgB,
    });

    const tenantOrgResponse = await api.get('/org-for-auth', {
      headers: {
        Host: `${subA}.example.com`,
      },
    });
    assert.strictEqual(tenantOrgResponse.status, 200);
    assert.strictEqual(tenantOrgResponse.data[0].id, orgA);

    const userAToken = await signupTenantUser({
      subdomain: subA,
      organizationsId: orgA,
      email: `tenant-a-${timestamp}@example.com`,
      roleId: adminRoleId,
      superAdminToken,
    });
    const userBToken = await signupTenantUser({
      subdomain: subB,
      organizationsId: orgB,
      email: `tenant-b-${timestamp}@example.com`,
      roleId: adminRoleId,
      superAdminToken,
    });

    const intakeResponse = await api.post('/contact-form', buildContactPayload(timestamp), {
      headers: {
        Host: `${subA}.example.com`,
        Referer: `http://${subA}.example.com/`,
      },
    });
    assert.strictEqual(intakeResponse.status, 200, JSON.stringify(intakeResponse.data));
    const { leadId } = intakeResponse.data;
    assert.ok(leadId, 'Expected contact form to create a lead');

    const readOwnLead = await api.get(`/leads/${leadId}`, {
      headers: {
        Authorization: `Bearer ${userAToken}`,
      },
    });
    assert.strictEqual(readOwnLead.status, 200);
    assert.strictEqual(readOwnLead.data.id, leadId);

    const readOtherLead = await api.get(`/leads/${leadId}`, {
      headers: {
        Authorization: `Bearer ${userBToken}`,
      },
    });
    assert.strictEqual(readOtherLead.status, 404);

    const updateOtherLead = await api.put(
      `/leads/${leadId}`,
      {
        id: leadId,
        data: {
          status: 'new',
        },
      },
      {
        headers: {
          Authorization: `Bearer ${userBToken}`,
        },
      },
    );
    assert.ok([400, 404].includes(updateOtherLead.status), `Expected cross-org update to fail, got ${updateOtherLead.status}`);

    const deleteOtherLead = await api.delete(`/leads/${leadId}`, {
      headers: {
        Authorization: `Bearer ${userBToken}`,
      },
    });
    assert.strictEqual(deleteOtherLead.status, 404);

    const visibleRowsForOwner = await sql(userAToken, `select id from leads where id = '${leadId}'`);
    assert.strictEqual(visibleRowsForOwner.length, 1, 'Expected owner SQL query to return the lead');

    const visibleRowsForOtherOrg = await sql(userBToken, `select id from leads where id = '${leadId}'`);
    assert.strictEqual(visibleRowsForOtherOrg.length, 0, 'Expected RLS to hide the lead from another organization');
  });
});
