import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import {
  collectNotionPages,
  createNotionOAuthAttempt,
  flattenNotionProperty,
  notionAuthorizationUrl,
  notionConnectionMetadata,
  verifyNotionOAuthAttempt,
} from '../src/services/notion';

describe('Notion OAuth decisions', () => {
  it('uses one-time state and browser binding without inventing unsupported PKCE parameters', () => {
    let byte = 0;
    const attempt = createNotionOAuthAttempt(
      new Date('2026-08-13T12:00:00.000Z'),
      (size) => Buffer.alloc(size, byte += 1),
    );
    assert.notEqual(attempt.state, attempt.verifier);
    assert.notEqual(attempt.stateHash, attempt.state);
    assert.notEqual(attempt.verifierHash, attempt.verifier);
    assert.equal(verifyNotionOAuthAttempt(
      attempt.verifier,
      attempt.verifierHash,
      attempt.expiresAt,
      new Date('2026-08-13T12:09:59.000Z'),
    ), true);
    assert.equal(verifyNotionOAuthAttempt('tampered', attempt.verifierHash, attempt.expiresAt), false);
    assert.equal(verifyNotionOAuthAttempt(
      attempt.verifier,
      attempt.verifierHash,
      attempt.expiresAt,
      new Date('2026-08-13T12:10:00.000Z'),
    ), false);
    const url = new URL(notionAuthorizationUrl({
      clientId: 'client-id',
      redirectUri: 'https://pig.example/api/imports/notion/oauth/callback',
      state: attempt.state,
    }));
    assert.equal(url.searchParams.get('state'), attempt.state);
    assert.equal(url.searchParams.has('code_challenge'), false);
    assert.equal(url.searchParams.has('code_verifier'), false);
  });

  it('redacts every credential-shaped field from connection metadata', () => {
    const storedConnection = {
      id: 'connection-id',
      workspaceId: 'workspace-id',
      workspaceName: 'Sales',
      workspaceIcon: null,
      createdAt: new Date('2026-08-13T12:00:00.000Z'),
      credentialsEncrypted: 'v1.secret.envelope',
      accessToken: 'never-return',
    };
    const metadata = notionConnectionMetadata(storedConnection);
    const serialized = JSON.stringify(metadata);
    assert.equal(serialized.includes('never-return'), false);
    assert.equal(serialized.includes('envelope'), false);
  });
});

describe('Notion pagination and flattening decisions', () => {
  it('follows cursors in order and stops after the declared import bound', async () => {
    const cursors: Array<string | undefined> = [];
    const rows = await collectNotionPages(async (cursor) => {
      cursors.push(cursor);
      return cursor
        ? { results: [{ id: '2' }, { id: '3' }], has_more: false, next_cursor: null }
        : { results: [{ id: '1' }], has_more: true, next_cursor: 'next' };
    }, 2);
    assert.deepEqual(cursors, [undefined, 'next']);
    assert.deepEqual(rows.map((row) => row.id), ['1', '2']);
  });

  it('maps supported values explicitly and rejects unstable property types', () => {
    assert.deepEqual(flattenNotionProperty({
      type: 'title',
      title: [{ plain_text: 'Acme' }, { plain_text: ' Compute' }],
    }), { value: 'Acme Compute' });
    assert.deepEqual(flattenNotionProperty({
      type: 'date',
      date: { start: '2026-09-01', end: '2026-09-30' },
    }), { value: '2026-09-01/2026-09-30' });
    assert.deepEqual(flattenNotionProperty({
      type: 'formula',
      formula: { type: 'number', number: 12.5 },
    }), { value: '12.5' });
    assert.match(flattenNotionProperty({ type: 'button', button: {} }).error ?? '', /stable tabular/);
  });
});
