/**
 * Portal JWT verification — locks in the audience/issuer hardening shipped
 * in fix(auth): a token without `aud: 'portal'` + `iss: 'pn-crm'` claims
 * must NOT verify, even if it's signed with the correct shared secret.
 *
 * Without these claims the CRM (better-auth) and portal sessions are
 * structurally identical, so a portal token could be replayed against any
 * `verifyPortalToken` consumer (and vice versa).
 */
import { describe, expect, it } from 'vitest';
import { SignJWT } from 'jose';

import { createPortalToken, verifyPortalToken } from '@/lib/portal/auth';

const SECRET = new TextEncoder().encode(process.env.BETTER_AUTH_SECRET);

const SESSION = {
  clientId: '11111111-1111-1111-1111-111111111111',
  portId: '22222222-2222-2222-2222-222222222222',
  email: 'client@example.com',
};

describe('portal JWT', () => {
  it('round-trips a token signed with createPortalToken', async () => {
    const token = await createPortalToken(SESSION);
    const verified = await verifyPortalToken(token);
    expect(verified).toMatchObject(SESSION);
  });

  it('rejects a token missing the `aud: portal` claim', async () => {
    // Issuer present, audience absent — exactly the shape an old (pre-fix)
    // portal session would have.
    const token = await new SignJWT(SESSION as unknown as Record<string, unknown>)
      .setProtectedHeader({ alg: 'HS256' })
      .setIssuer('pn-crm')
      .setExpirationTime('24h')
      .setIssuedAt()
      .sign(SECRET);

    expect(await verifyPortalToken(token)).toBeNull();
  });

  it('rejects a token missing the `iss: pn-crm` claim', async () => {
    const token = await new SignJWT(SESSION as unknown as Record<string, unknown>)
      .setProtectedHeader({ alg: 'HS256' })
      .setAudience('portal')
      .setExpirationTime('24h')
      .setIssuedAt()
      .sign(SECRET);

    expect(await verifyPortalToken(token)).toBeNull();
  });

  it('rejects a token with the wrong audience (CRM session replay shape)', async () => {
    // What a better-auth session token might roughly look like — same secret,
    // different audience. Must not verify against the portal path.
    const token = await new SignJWT(SESSION as unknown as Record<string, unknown>)
      .setProtectedHeader({ alg: 'HS256' })
      .setAudience('crm')
      .setIssuer('pn-crm')
      .setExpirationTime('24h')
      .setIssuedAt()
      .sign(SECRET);

    expect(await verifyPortalToken(token)).toBeNull();
  });

  it('rejects a token with the wrong issuer', async () => {
    const token = await new SignJWT(SESSION as unknown as Record<string, unknown>)
      .setProtectedHeader({ alg: 'HS256' })
      .setAudience('portal')
      .setIssuer('attacker')
      .setExpirationTime('24h')
      .setIssuedAt()
      .sign(SECRET);

    expect(await verifyPortalToken(token)).toBeNull();
  });

  it('rejects garbage', async () => {
    expect(await verifyPortalToken('not.a.jwt')).toBeNull();
    expect(await verifyPortalToken('')).toBeNull();
  });
});
