import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
  validateConfig,
  detectFromEnv,
  DEFAULT_LOCAL_CONFIG,
  DEFAULT_STANDALONE_CONFIG,
  DEFAULT_FEDERATED_CONFIG,
} from './mosaic-config.js';

describe('validateConfig — tier enum', () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  let stderrSpy: any;

  beforeEach(() => {
    stderrSpy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
  });

  afterEach(() => {
    stderrSpy.mockRestore();
  });

  it('accepts tier="local"', () => {
    const result = validateConfig({
      tier: 'local',
      storage: { type: 'pglite', dataDir: '.mosaic/storage-pglite' },
      queue: { type: 'local', dataDir: '.mosaic/queue' },
      memory: { type: 'keyword' },
    });
    expect(result.tier).toBe('local');
  });

  it('accepts tier="standalone"', () => {
    const result = validateConfig({
      tier: 'standalone',
      storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' },
      queue: { type: 'bullmq' },
      memory: { type: 'keyword' },
    });
    expect(result.tier).toBe('standalone');
  });

  it('accepts tier="federated"', () => {
    const result = validateConfig({
      tier: 'federated',
      storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5433/mosaic' },
      queue: { type: 'bullmq' },
      memory: { type: 'pgvector' },
    });
    expect(result.tier).toBe('federated');
  });

  it('accepts deprecated tier="team" as alias for "standalone" and emits a deprecation warning', () => {
    const result = validateConfig({
      tier: 'team',
      storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' },
      queue: { type: 'bullmq' },
      memory: { type: 'keyword' },
    });
    expect(result.tier).toBe('standalone');
    expect(stderrSpy).toHaveBeenCalledWith(expect.stringContaining('DEPRECATED'));
  });

  it('rejects an invalid tier with an error listing all three valid values', () => {
    expect(() =>
      validateConfig({
        tier: 'invalid',
        storage: { type: 'postgres', url: 'postgresql://mosaic:mosaic@localhost:5432/mosaic' },
        queue: { type: 'bullmq' },
        memory: { type: 'keyword' },
      }),
    ).toThrow(/local.*standalone.*federated|federated.*standalone.*local/);
  });

  it('error message for invalid tier mentions all three valid values', () => {
    let message = '';
    try {
      validateConfig({
        tier: 'invalid',
        storage: { type: 'postgres', url: 'postgresql://...' },
        queue: { type: 'bullmq' },
        memory: { type: 'keyword' },
      });
    } catch (err) {
      message = err instanceof Error ? err.message : String(err);
    }
    expect(message).toContain('"local"');
    expect(message).toContain('"standalone"');
    expect(message).toContain('"federated"');
  });
});

describe('DEFAULT_* config constants', () => {
  it('DEFAULT_LOCAL_CONFIG has tier="local"', () => {
    expect(DEFAULT_LOCAL_CONFIG.tier).toBe('local');
  });

  it('DEFAULT_STANDALONE_CONFIG has tier="standalone"', () => {
    expect(DEFAULT_STANDALONE_CONFIG.tier).toBe('standalone');
  });

  it('DEFAULT_FEDERATED_CONFIG has tier="federated" and pgvector memory', () => {
    expect(DEFAULT_FEDERATED_CONFIG.tier).toBe('federated');
    expect(DEFAULT_FEDERATED_CONFIG.memory.type).toBe('pgvector');
  });

  it('DEFAULT_FEDERATED_CONFIG uses port 5433 (distinct from standalone 5432)', () => {
    const url = (DEFAULT_FEDERATED_CONFIG.storage as { url: string }).url;
    expect(url).toContain('5433');
  });

  it('DEFAULT_FEDERATED_CONFIG has enableVector=true on storage', () => {
    const storage = DEFAULT_FEDERATED_CONFIG.storage as {
      type: string;
      url: string;
      enableVector?: boolean;
    };
    expect(storage.enableVector).toBe(true);
  });
});

describe('detectFromEnv — tier env-var routing', () => {
  const originalEnv = process.env;

  beforeEach(() => {
    // Work on a fresh copy so individual tests can set/delete keys freely.
    process.env = { ...originalEnv };
    delete process.env['MOSAIC_STORAGE_TIER'];
    delete process.env['DATABASE_URL'];
    delete process.env['VALKEY_URL'];
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  it('no env vars → returns local config', () => {
    const config = detectFromEnv();
    expect(config.tier).toBe('local');
    expect(config.storage.type).toBe('pglite');
    expect(config.memory.type).toBe('keyword');
  });

  it('MOSAIC_STORAGE_TIER=federated alone → returns federated config with enableVector=true', () => {
    process.env['MOSAIC_STORAGE_TIER'] = 'federated';
    const config = detectFromEnv();
    expect(config.tier).toBe('federated');
    expect(config.memory.type).toBe('pgvector');
    const storage = config.storage as { type: string; enableVector?: boolean };
    expect(storage.enableVector).toBe(true);
  });

  it('MOSAIC_STORAGE_TIER=federated + DATABASE_URL → uses the URL and still has enableVector=true', () => {
    process.env['MOSAIC_STORAGE_TIER'] = 'federated';
    process.env['DATABASE_URL'] = 'postgresql://custom:pass@db.example.com:5432/mydb';
    const config = detectFromEnv();
    expect(config.tier).toBe('federated');
    const storage = config.storage as { type: string; url: string; enableVector?: boolean };
    expect(storage.url).toBe('postgresql://custom:pass@db.example.com:5432/mydb');
    expect(storage.enableVector).toBe(true);
    expect(config.memory.type).toBe('pgvector');
  });

  it('MOSAIC_STORAGE_TIER=standalone alone → returns standalone-shaped config (not local)', () => {
    process.env['MOSAIC_STORAGE_TIER'] = 'standalone';
    const config = detectFromEnv();
    expect(config.tier).toBe('standalone');
    expect(config.storage.type).toBe('postgres');
    expect(config.memory.type).toBe('keyword');
  });
});
