/**
 * Security regression: dismissAlert / acknowledgeAlert must filter by
 * portId so an alert UUID from port A can't be mutated by a session
 * scoped to port B.
 */

import { describe, it, expect, beforeAll } from 'vitest';
import { eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { alerts } from '@/lib/db/schema/insights';
import { user } from '@/lib/db/schema/users';
import { dismissAlert, acknowledgeAlert } from '@/lib/services/alerts.service';
import { makePort } from '../helpers/factories';

let TEST_USER_ID = '';

beforeAll(async () => {
  // dismissedBy / acknowledgedBy reference user.id; pull any seeded user.
  const [u] = await db.select({ id: user.id }).from(user).limit(1);
  if (!u) throw new Error('No user available; run pnpm db:seed first');
  TEST_USER_ID = u.id;
});

async function makeAlert(portId: string) {
  const [row] = await db
    .insert(alerts)
    .values({
      portId,
      ruleId: 'document_overdue',
      severity: 'medium',
      title: 'Test alert',
      link: '/test',
      fingerprint: `fp-${Math.random().toString(36).slice(2)}`,
      metadata: {},
    })
    .returning({ id: alerts.id });
  if (!row) throw new Error('failed to insert alert');
  return row.id;
}

describe('alerts service - tenant isolation', () => {
  it('dismissAlert is a no-op when called with the wrong portId', async () => {
    const portA = await makePort();
    const portB = await makePort();
    const alertId = await makeAlert(portA.id);

    // Attacker session scoped to port B tries to dismiss port A's alert.
    await dismissAlert(alertId, portB.id, TEST_USER_ID);

    const after = await db.query.alerts.findFirst({ where: eq(alerts.id, alertId) });
    expect(after?.dismissedAt).toBeNull();
    expect(after?.dismissedBy).toBeNull();
  });

  it('dismissAlert succeeds when portId matches', async () => {
    const port = await makePort();
    const alertId = await makeAlert(port.id);

    await dismissAlert(alertId, port.id, TEST_USER_ID);

    const after = await db.query.alerts.findFirst({ where: eq(alerts.id, alertId) });
    expect(after?.dismissedAt).not.toBeNull();
    expect(after?.dismissedBy).toBe(TEST_USER_ID);
  });

  it('acknowledgeAlert is a no-op when called with the wrong portId', async () => {
    const portA = await makePort();
    const portB = await makePort();
    const alertId = await makeAlert(portA.id);

    await acknowledgeAlert(alertId, portB.id, TEST_USER_ID);

    const after = await db.query.alerts.findFirst({ where: eq(alerts.id, alertId) });
    expect(after?.acknowledgedAt).toBeNull();
    expect(after?.acknowledgedBy).toBeNull();
  });

  it('acknowledgeAlert succeeds when portId matches', async () => {
    const port = await makePort();
    const alertId = await makeAlert(port.id);

    await acknowledgeAlert(alertId, port.id, TEST_USER_ID);

    const after = await db.query.alerts.findFirst({ where: eq(alerts.id, alertId) });
    expect(after?.acknowledgedAt).not.toBeNull();
    expect(after?.acknowledgedBy).toBe(TEST_USER_ID);
  });
});
