import {
  describe,
  expect,
  it,
  vi
} from 'vitest';

import {
  applyBrowserDatabaseSchema,
  ensureObjectStoreDuringUpgrade,
  ensureStoreIndex
} from './browser-database-schema';

describe('browser-database-schema', () => {
  it('reuses the upgrade transaction when an object store already exists', () => {
    const existingStore = { indexNames: { contains: () => false } };
    const database = {
      objectStoreNames: { contains: (name: string) => name === 'messages' },
      createObjectStore: vi.fn(),
      transaction: vi.fn()
    };
    const upgradeTransaction = {
      objectStore: vi.fn(() => existingStore)
    };
    const store = ensureObjectStoreDuringUpgrade(
      database as unknown as IDBDatabase,
      upgradeTransaction as unknown as IDBTransaction,
      'messages',
      { keyPath: 'id' }
    );

    expect(store).toBe(existingStore);
    expect(upgradeTransaction.objectStore).toHaveBeenCalledWith('messages');
    expect(database.createObjectStore).not.toHaveBeenCalled();
    expect(database.transaction).not.toHaveBeenCalled();
  });

  it('creates missing object stores during upgrade', () => {
    const createdStore = { indexNames: { contains: () => false } };
    const database = {
      objectStoreNames: { contains: () => false },
      createObjectStore: vi.fn(() => createdStore),
      transaction: vi.fn()
    };
    const upgradeTransaction = {
      objectStore: vi.fn()
    };
    const store = ensureObjectStoreDuringUpgrade(
      database as unknown as IDBDatabase,
      upgradeTransaction as unknown as IDBTransaction,
      'customEmojis',
      { keyPath: 'id' }
    );

    expect(store).toBe(createdStore);
    expect(database.createObjectStore).toHaveBeenCalledWith('customEmojis', { keyPath: 'id' });
    expect(upgradeTransaction.objectStore).not.toHaveBeenCalled();
    expect(database.transaction).not.toHaveBeenCalled();
  });

  it('creates missing indexes on an existing store', () => {
    const store = {
      indexNames: { contains: () => false },
      createIndex: vi.fn()
    };

    ensureStoreIndex(store as unknown as IDBObjectStore, 'roomId', 'roomId');

    expect(store.createIndex).toHaveBeenCalledWith('roomId', 'roomId', { unique: false });
  });

  it('applies the full schema through the upgrade transaction only', () => {
    const stores = new Map<string, { indexNames: { contains: (name: string) => boolean }; createIndex: ReturnType<typeof vi.fn> }>();
    const database = {
      objectStoreNames: {
        contains: (name: string) => stores.has(name)
      },
      createObjectStore: vi.fn((name: string) => {
        const store = {
          indexNames: { contains: () => false },
          createIndex: vi.fn()
        };

        stores.set(name, store);

        return store;
      }),
      transaction: vi.fn()
    };
    const upgradeTransaction = {
      objectStore: vi.fn((name: string) => {
        const store = stores.get(name);

        if (!store) {
          throw new Error(`Missing store ${name}`);
        }

        return store;
      })
    };

    stores.set('messages', {
      indexNames: { contains: () => true },
      createIndex: vi.fn()
    });

    stores.set('users', {
      indexNames: { contains: () => true },
      createIndex: vi.fn()
    });

    expect(() => applyBrowserDatabaseSchema(
      database as unknown as IDBDatabase,
      upgradeTransaction as unknown as IDBTransaction
    )).not.toThrow();

    expect(database.transaction).not.toHaveBeenCalled();
    expect(upgradeTransaction.objectStore).toHaveBeenCalledWith('messages');
    expect(database.createObjectStore).toHaveBeenCalledWith('customEmojis', { keyPath: 'id' });
  });
});
