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

// Must hoist mocks so they're available when vi.mock factory runs
const { mockDatabase, mockDatabases, MockCosmosClient } = vi.hoisted(() => {
  const mockContainer = { id: 'test-container' };
  const mockDatabase = {
    container: vi.fn(() => mockContainer),
    containers: { createIfNotExists: vi.fn() },
  };
  const mockDatabases = { createIfNotExists: vi.fn(() => ({ database: mockDatabase })) };
  const MockCosmosClient = vi.fn(() => ({
    database: vi.fn(() => mockDatabase),
    databases: mockDatabases,
  }));
  return { mockDatabase, mockDatabases, MockCosmosClient };
});

vi.mock('@azure/cosmos', () => ({
  CosmosClient: MockCosmosClient,
  PartitionKeyDefinition: class {},
}));

import {
  getCosmosClient,
  getDatabase,
  getContainer,
  _resetClient,
  registerContainers,
  getRegisteredContainer,
  initializeAllContainers,
  _resetRegistry,
} from '../index.js';

describe('cosmos client', () => {
  beforeEach(() => {
    _resetClient();
    _resetRegistry();
    MockCosmosClient.mockClear();
    process.env.COSMOS_ENDPOINT = 'https://test.documents.azure.com:443/';
    process.env.COSMOS_KEY = 'test-key==';
    process.env.COSMOS_DATABASE = 'testdb';
  });

  afterEach(() => {
    delete process.env.COSMOS_ENDPOINT;
    delete process.env.COSMOS_KEY;
    delete process.env.COSMOS_DATABASE;
  });

  it('getCosmosClient creates singleton', () => {
    const c1 = getCosmosClient();
    const c2 = getCosmosClient();
    expect(c1).toBe(c2);
    expect(MockCosmosClient).toHaveBeenCalledTimes(1);
    expect(MockCosmosClient).toHaveBeenCalledWith({
      endpoint: 'https://test.documents.azure.com:443/',
      key: 'test-key==',
    });
  });

  it('getCosmosClient throws without COSMOS_ENDPOINT', () => {
    delete process.env.COSMOS_ENDPOINT;
    expect(() => getCosmosClient()).toThrow('COSMOS_ENDPOINT is required');
  });

  it('getCosmosClient throws without COSMOS_KEY', () => {
    delete process.env.COSMOS_KEY;
    expect(() => getCosmosClient()).toThrow('COSMOS_KEY is required');
  });

  it('getDatabase uses COSMOS_DATABASE env var', () => {
    const db = getDatabase();
    expect(db).toBeDefined();
  });

  it('getDatabase defaults to lysnrai', () => {
    _resetClient();
    delete process.env.COSMOS_DATABASE;
    getDatabase();
    // Client was called, database accessed
    expect(MockCosmosClient).toHaveBeenCalled();
  });

  it('getContainer returns container by name', () => {
    const c = getContainer('users');
    expect(c).toBeDefined();
  });

  it('_resetClient clears singleton', () => {
    getCosmosClient();
    expect(MockCosmosClient).toHaveBeenCalledTimes(1);
    _resetClient();
    getCosmosClient();
    expect(MockCosmosClient).toHaveBeenCalledTimes(2);
  });
});

describe('container registry', () => {
  beforeEach(() => {
    _resetClient();
    _resetRegistry();
    MockCosmosClient.mockClear();
    process.env.COSMOS_ENDPOINT = 'https://test.documents.azure.com:443/';
    process.env.COSMOS_KEY = 'test-key==';
    process.env.COSMOS_DATABASE = 'testdb';
  });

  afterEach(() => {
    delete process.env.COSMOS_ENDPOINT;
    delete process.env.COSMOS_KEY;
    delete process.env.COSMOS_DATABASE;
  });

  it('registerContainers stores definitions', () => {
    registerContainers({
      users: { partitionKeyPath: '/productId' },
      tokens: { partitionKeyPath: '/userId' },
    });
    // Should not throw
    const c = getRegisteredContainer('users');
    expect(c).toBeDefined();
  });

  it('getRegisteredContainer throws for unknown name', () => {
    expect(() => getRegisteredContainer('nope')).toThrow("Unknown container 'nope'");
  });

  it('getRegisteredContainer caches container instances', () => {
    registerContainers({ items: { partitionKeyPath: '/id' } });
    const c1 = getRegisteredContainer('items');
    const c2 = getRegisteredContainer('items');
    expect(c1).toBe(c2);
  });

  it('initializeAllContainers creates database and containers', async () => {
    registerContainers({
      users: { partitionKeyPath: '/productId' },
      audit: { partitionKeyPath: '/productId', defaultTtl: 86400 },
    });

    await initializeAllContainers();

    expect(mockDatabases.createIfNotExists).toHaveBeenCalledWith({ id: 'testdb' });
    expect(mockDatabase.containers.createIfNotExists).toHaveBeenCalledTimes(2);
  });

  it('_resetRegistry clears all', () => {
    registerContainers({ x: { partitionKeyPath: '/id' } });
    _resetRegistry();
    expect(() => getRegisteredContainer('x')).toThrow("Unknown container 'x'");
  });
});
