local createLibTest = require('/apis/libtest');
local createTrapGpt = require('/apis/libtrapgpt');

local testlib = createLibTest({ ... });

local function fakeSettings(values)
  values = values or {};
  return {
    get = function(key) return values[key]; end,
  };
end

local function fakeAi(replies)
  local calls = {};
  local idx = 0;
  return {
    ask = function(prompt, options)
      idx = idx + 1;
      calls[#calls + 1] = { prompt = prompt, options = options };
      local reply = replies[idx];
      if reply == false then return false, 'ai failed'; end
      return true, { reply = reply or 'ok' };
    end,
    calls = calls,
  };
end

local function fakeChatBox()
  local messages = {};
  return {
    sendMessage = function(message, prefix)
      messages[#messages + 1] = { message = message, prefix = prefix };
      return true;
    end,
    messages = messages,
  };
end

testlib.test('trapgpt queues visible chat messages only', function()
  local ai = fakeAi({ 'hi' });
  local chatBox = fakeChatBox();
  local bot = createTrapGpt({
    ai = ai,
    chatBox = chatBox,
    settings = fakeSettings({ trapgpt_throttle_seconds = 0 }),
    sleep = function() end,
    now = function() return 10; end,
  });

  testlib.assertEquals(bot.onChat('alice', 'hello', 'uuid-a', false), true);
  testlib.assertEquals(bot.onChat('bob', '$secret', 'uuid-b', true), false);
  testlib.assertEquals(bot.pendingCount(), 1);
end);

testlib.test('trapgpt sends queued messages in one prompt', function()
  local ai = fakeAi({ 'bonjour' });
  local chatBox = fakeChatBox();
  local bot = createTrapGpt({
    ai = ai,
    chatBox = chatBox,
    settings = fakeSettings({ ['trapgpt.throttle_seconds'] = 0 }),
    sleep = function() end,
    now = function() return 20; end,
  });

  bot.onChat('alice', 'one', nil, false);
  bot.onChat('bob', 'two', nil, false);
  local processed, reply = bot.processOnce();

  testlib.assertEquals(processed, true);
  testlib.assertEquals(reply, 'bonjour');
  testlib.assertEquals(#ai.calls, 1);
  testlib.assertTrue(ai.calls[1].prompt:find('alice: one', 1, true) ~= nil);
  testlib.assertTrue(ai.calls[1].prompt:find('bob: two', 1, true) ~= nil);
  testlib.assertEquals(ai.calls[1].options.sessionSettingKey, 'trapgpt.opencc.session_id');
  testlib.assertEquals(#chatBox.messages, 1);
  testlib.assertEquals(chatBox.messages[1].prefix, 'TrapGPT');
end);

testlib.test('trapgpt only sends missing messages after success', function()
  local ai = fakeAi({ 'first', 'second' });
  local bot = createTrapGpt({
    ai = ai,
    chatBox = fakeChatBox(),
    settings = fakeSettings({ ['trapgpt.throttle_seconds'] = 0 }),
    sleep = function() end,
    now = function() return 30; end,
  });

  bot.onChat('alice', 'one', nil, false);
  bot.processOnce();
  bot.onChat('bob', 'two', nil, false);
  bot.processOnce();

  testlib.assertEquals(#ai.calls, 2);
  testlib.assertTrue(ai.calls[2].prompt:find('bob: two', 1, true) ~= nil);
  testlib.assertTrue(ai.calls[2].prompt:find('alice: one', 1, true) == nil);
end);

testlib.test('trapgpt keeps queue when ai fails', function()
  local ai = fakeAi({ false, 'retry' });
  local chatBox = fakeChatBox();
  local bot = createTrapGpt({
    ai = ai,
    chatBox = chatBox,
    settings = fakeSettings({ ['trapgpt.throttle_seconds'] = 0 }),
    sleep = function() end,
    now = function() return 40; end,
    log = function() end,
  });

  bot.onChat('alice', 'one', nil, false);
  testlib.assertEquals(bot.processOnce(), false);
  testlib.assertEquals(bot.pendingCount(), 1);
  testlib.assertEquals(bot.processOnce(), true);
  testlib.assertEquals(bot.pendingCount(), 0);
  testlib.assertEquals(#chatBox.messages, 1);
end);

testlib.test('trapgpt does not send SILENCE replies', function()
  local ai = fakeAi({ 'SILENCE' });
  local chatBox = fakeChatBox();
  local bot = createTrapGpt({
    ai = ai,
    chatBox = chatBox,
    settings = fakeSettings({ ['trapgpt.throttle_seconds'] = 0 }),
    sleep = function() end,
    now = function() return 50; end,
  });

  bot.onChat('alice', 'one', nil, false);
  testlib.assertEquals(bot.processOnce(), true);
  testlib.assertEquals(#chatBox.messages, 0);
end);

testlib.test('trapgpt truncates long replies', function()
  local ai = fakeAi({ 'abcdefghij' });
  local chatBox = fakeChatBox();
  local bot = createTrapGpt({
    ai = ai,
    chatBox = chatBox,
    settings = fakeSettings({
      ['trapgpt.throttle_seconds'] = 0,
      ['trapgpt.max_reply_chars'] = 6,
    }),
    sleep = function() end,
    now = function() return 60; end,
  });

  bot.onChat('alice', 'one', nil, false);
  bot.processOnce();
  testlib.assertEquals(chatBox.messages[1].message, 'abc...');
end);

testlib.run();
