import {
  describe,
  it,
  expect
} from 'vitest';
import type { Message } from '../../../../shared-kernel';
import {
  buildMessageHeadState,
  computeMessageHeadHash,
  getMessageRevision,
  inventoryNeedsRefresh,
  resolveMessageRevision
} from './message-integrity.rules';

function createMessage(overrides: Partial<Message> = {}): Message {
  return {
    id: 'message-1',
    roomId: 'room-1',
    senderId: 'user-1',
    senderName: 'User 1',
    content: 'hello',
    timestamp: 1_000,
    reactions: [],
    isDeleted: false,
    ...overrides
  };
}

describe('message-integrity.rules', () => {
  it('defaults missing revision to zero', () => {
    expect(getMessageRevision(createMessage())).toBe(0);
    expect(getMessageRevision(createMessage({ revision: 3 }))).toBe(3);
  });

  it('produces stable head hashes for the same canonical state', async () => {
    const message = createMessage({ revision: 0 });
    const state = buildMessageHeadState(message, 0);
    const first = await computeMessageHeadHash(state);
    const second = await computeMessageHeadHash(state);

    expect(first).toBe(second);
    expect(first).toMatch(/^[a-f0-9]{64}$/);
  });

  it('changes head hash when content or revision changes', async () => {
    const original = await computeMessageHeadHash(buildMessageHeadState(createMessage({ revision: 0 }), 0));
    const edited = await computeMessageHeadHash(buildMessageHeadState(createMessage({
      content: 'edited',
      editedAt: 2_000,
      revision: 1
    }), 1));

    expect(edited).not.toBe(original);
  });

  it('requests refresh when revision is newer', () => {
    expect(inventoryNeedsRefresh(
      { ts: 10, rc: 0, ac: 0, revision: 1, headHash: 'aaa' },
      { ts: 10, rc: 0, ac: 0, revision: 0, headHash: 'aaa' }
    )).toBe(true);
  });

  it('requests refresh when revision matches but head hash differs', () => {
    expect(inventoryNeedsRefresh(
      { ts: 10, rc: 0, ac: 0, revision: 1, headHash: 'tampered' },
      { ts: 10, rc: 0, ac: 0, revision: 1, headHash: 'canonical' }
    )).toBe(true);
  });

  it('does not request refresh when revision and head hash match', () => {
    expect(inventoryNeedsRefresh(
      { ts: 10, rc: 0, ac: 0, revision: 2, headHash: 'same' },
      { ts: 10, rc: 0, ac: 0, revision: 2, headHash: 'same' }
    )).toBe(false);
  });

  it('falls back to legacy timestamp comparison when remote inventory lacks integrity fields', () => {
    expect(inventoryNeedsRefresh(
      { ts: 20, rc: 0, ac: 0 },
      { ts: 10, rc: 0, ac: 0, revision: 0, headHash: 'hash' }
    )).toBe(true);

    expect(inventoryNeedsRefresh(
      { ts: 10, rc: 1, ac: 0 },
      { ts: 10, rc: 0, ac: 0, revision: 0, headHash: 'hash' }
    )).toBe(true);
  });

  it('resolves next revision for create, edit, and delete flows', () => {
    expect(resolveMessageRevision(undefined, 'create')).toBe(0);
    expect(resolveMessageRevision(createMessage({ revision: 0 }), 'author-edit')).toBe(1);
    expect(resolveMessageRevision(createMessage({ revision: 4 }), 'moderate-delete')).toBe(5);
  });
});
