import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TranslationModule } from './index.js';

// Mock logger
vi.mock('../../utils/logger.js', () => ({
  createModuleLogger: () => ({
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    debug: vi.fn(),
  }),
}));

// Mock file-ops for glossary persistence
const mockReadJSON = vi.fn();
const mockAtomicWriteJSON = vi.fn();
vi.mock('../data-store/file-ops.js', () => ({
  readJSON: (...args: any[]) => mockReadJSON(...args),
  atomicWriteJSON: (...args: any[]) => mockAtomicWriteJSON(...args),
}));

function createMockOpenRouterClient(responseContent: string | null = 'A woman on the beach') {
  return {
    chatCompletion: vi.fn().mockResolvedValue({
      id: 'test-id',
      choices: [{ message: { role: 'assistant', content: responseContent }, finish_reason: 'stop' }],
      usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
    }),
  } as any;
}

function createMockDataStore(dataDir = '/tmp/test-data') {
  return { dataDir } as any;
}

describe('TranslationModule', () => {
  let module: TranslationModule;
  let mockClient: ReturnType<typeof createMockOpenRouterClient>;
  let mockStore: ReturnType<typeof createMockDataStore>;

  beforeEach(() => {
    vi.clearAllMocks();
    mockReadJSON.mockResolvedValue(null);
    mockAtomicWriteJSON.mockResolvedValue(undefined);
    mockClient = createMockOpenRouterClient();
    mockStore = createMockDataStore();
    module = new TranslationModule(mockStore, mockClient);
  });

  describe('translatePrompt', () => {
    it('should translate a Turkish prompt to English via OpenRouter', async () => {
      const result = await module.translatePrompt('Plajda yürüyen bir kadın');

      expect(result.original).toBe('Plajda yürüyen bir kadın');
      expect(result.translated).toBe('A woman on the beach');
      expect(mockClient.chatCompletion).toHaveBeenCalledOnce();

      const callArgs = mockClient.chatCompletion.mock.calls[0];
      expect(callArgs[0]).toHaveLength(2);
      expect(callArgs[0][0].role).toBe('system');
      expect(callArgs[0][1].role).toBe('user');
      expect(callArgs[0][1].content).toBe('Plajda yürüyen bir kadın');
      expect(callArgs[1]).toEqual({ temperature: 0.7, max_tokens: 500 });
    });

    it('should apply glossary terms before sending to OpenRouter', async () => {
      mockReadJSON.mockResolvedValue({ plaj: 'beach', kadın: 'woman' });

      await module.translatePrompt('plaj ve kadın');

      const callArgs = mockClient.chatCompletion.mock.calls[0];
      // Glossary should have replaced terms in the user message
      expect(callArgs[0][1].content).toBe('beach ve woman');
    });

    it('should return original prompt as fallback when OpenRouter fails', async () => {
      mockClient.chatCompletion.mockRejectedValue(new Error('API error'));

      const result = await module.translatePrompt('Plajda yürüyen bir kadın');

      expect(result.original).toBe('Plajda yürüyen bir kadın');
      expect(result.translated).toBe('Plajda yürüyen bir kadın');
    });

    it('should return fallback when OpenRouter returns empty content', async () => {
      mockClient = createMockOpenRouterClient('');
      module = new TranslationModule(mockStore, mockClient);

      const result = await module.translatePrompt('Test prompt');

      expect(result.original).toBe('Test prompt');
      expect(result.translated).toBe('Test prompt');
    });

    it('should return fallback when OpenRouter returns null content', async () => {
      mockClient = createMockOpenRouterClient(null);
      module = new TranslationModule(mockStore, mockClient);

      const result = await module.translatePrompt('Test prompt');

      expect(result.original).toBe('Test prompt');
      expect(result.translated).toBe('Test prompt');
    });
  });

  describe('getGlossary', () => {
    it('should return empty object when no glossary file exists', async () => {
      mockReadJSON.mockResolvedValue(null);

      const glossary = await module.getGlossary();

      expect(glossary).toEqual({});
    });

    it('should return glossary from file', async () => {
      const terms = { plaj: 'beach', 'spor salonu': 'gym' };
      mockReadJSON.mockResolvedValue(terms);

      const glossary = await module.getGlossary();

      expect(glossary).toEqual(terms);
    });
  });

  describe('updateGlossary', () => {
    it('should write the full glossary to file', async () => {
      const terms = { plaj: 'beach', kafe: 'cafe' };

      await module.updateGlossary(terms);

      expect(mockAtomicWriteJSON).toHaveBeenCalledWith(
        expect.stringContaining('glossary.json'),
        terms,
      );
    });
  });

  describe('addGlossaryTerm', () => {
    it('should add a term to existing glossary', async () => {
      mockReadJSON.mockResolvedValue({ plaj: 'beach' });

      await module.addGlossaryTerm('kafe', 'cafe');

      expect(mockAtomicWriteJSON).toHaveBeenCalledWith(
        expect.stringContaining('glossary.json'),
        { plaj: 'beach', kafe: 'cafe' },
      );
    });

    it('should add a term when glossary is empty', async () => {
      mockReadJSON.mockResolvedValue(null);

      await module.addGlossaryTerm('plaj', 'beach');

      expect(mockAtomicWriteJSON).toHaveBeenCalledWith(
        expect.stringContaining('glossary.json'),
        { plaj: 'beach' },
      );
    });
  });

  describe('removeGlossaryTerm', () => {
    it('should remove a term from glossary', async () => {
      mockReadJSON.mockResolvedValue({ plaj: 'beach', kafe: 'cafe' });

      await module.removeGlossaryTerm('plaj');

      expect(mockAtomicWriteJSON).toHaveBeenCalledWith(
        expect.stringContaining('glossary.json'),
        { kafe: 'cafe' },
      );
    });

    it('should handle removing non-existent term gracefully', async () => {
      mockReadJSON.mockResolvedValue({ plaj: 'beach' });

      await module.removeGlossaryTerm('nonexistent');

      expect(mockAtomicWriteJSON).toHaveBeenCalledWith(
        expect.stringContaining('glossary.json'),
        { plaj: 'beach' },
      );
    });
  });

  describe('applyGlossary', () => {
    it('should replace Turkish terms with English equivalents', () => {
      const glossary = { plaj: 'beach', kadın: 'woman' };

      const result = module.applyGlossary('plaj ve kadın', glossary);

      expect(result).toBe('beach ve woman');
    });

    it('should be case-insensitive', () => {
      const glossary = { plaj: 'beach' };

      const result = module.applyGlossary('PLAJ sahnesinde', glossary);

      expect(result).toBe('beach sahnesinde');
    });

    it('should replace longer terms first to avoid partial matches', () => {
      const glossary = { 'spor salonu': 'gym', spor: 'sport' };

      const result = module.applyGlossary('spor salonu ve spor', glossary);

      expect(result).toBe('gym ve sport');
    });

    it('should return text unchanged when glossary is empty', () => {
      const result = module.applyGlossary('plajda yürüyen', {});

      expect(result).toBe('plajda yürüyen');
    });

    it('should handle special regex characters in terms', () => {
      const glossary = { 'test (özel)': 'test (special)' };

      const result = module.applyGlossary('bu bir test (özel) durum', glossary);

      expect(result).toBe('bu bir test (special) durum');
    });
  });
});
