local DR = select(2, ...)
local constants = DR.Modules["Core.Constants"]

--- Test mode exercises the full session flow without loot dropping:
---   /droll test          - enable test mode and open a session with a sample item
---   /droll test fake N   - inject N fake raider submissions (leader only)
---   /droll test resolve  - resolve the current test session
---   /droll test off      - disable test mode and purge fake ledger entries
--- Solo players are treated as their own raid leader while test mode is on.
--- Use /droll ping to verify comms with other players' addons.
local TestMode = {
  fakeCounter = 0,
}

local FAKE_GUID_PREFIX = "Test-Fake-"

local SAMPLE_ITEMS = {
  "|cffa335ee|Hitem:19019::::::::80:::::|h[Thunderfury, Blessed Blade of the Windseeker]|h|r",
  "|cffa335ee|Hitem:17182::::::::80:::::|h[Sulfuras, Hand of Ragnaros]|h|r",
  "|cffa335ee|Hitem:40395::::::::80:::::|h[Torch of Holy Fire]|h|r",
  "|cff8788ee|Hitem:22589::::::::80:::::|h[Atiesh, Greatstaff of the Guardian]|h|r",
}

local ROLLABLE_CATEGORIES = {
  constants.ROLL.CATEGORY.TMOG,
  constants.ROLL.CATEGORY.OFFSPEC,
  constants.ROLL.CATEGORY.UPGRADE,
  constants.ROLL.CATEGORY.BIS,
  constants.ROLL.CATEGORY.TIER_2P,
  constants.ROLL.CATEGORY.TIER_4P,
}

function TestMode:IsActive()
  return DR.Runtime.testMode == true
end

function TestMode:Enable()
  if self:IsActive() then
    return
  end
  DR.Runtime.testMode = true
  DR:Require("Services.Audit"):Append("TEST_MODE_ENABLED", {})
  DR:Print("Test mode ON. Sessions can be started solo; fake rolls via /droll test fake N.")
end

function TestMode:Disable()
  if not self:IsActive() then
    return
  end
  DR.Runtime.testMode = false
  self:PurgeFakeData()
  DR:Require("Services.Audit"):Append("TEST_MODE_DISABLED", {})
  DR:Print("Test mode OFF. Fake raider data purged.")
end

function TestMode:PurgeFakeData()
  local ledger = DR:Require("Services.Ledger")
  local db = DR.db.global

  for guid in pairs(db.weightLedger) do
    if type(guid) == "string" and guid:find(FAKE_GUID_PREFIX, 1, true) == 1 then
      ledger:RemoveEntry(guid)
    end
  end

  for guid, display in pairs(db.identityByGuid) do
    if type(guid) == "string" and guid:find(FAKE_GUID_PREFIX, 1, true) == 1 then
      db.identityByGuid[guid] = nil
      if display then
        db.guidByDisplay[display] = nil
      end
    end
  end
end

function TestMode:StartTestSession()
  local manager = DR:Require("Services.SessionManager")
  if not manager:IsRaidLeaderAuthority() then
    DR:Print("Test sessions require raid lead (or being solo).")
    return false
  end

  local link = SAMPLE_ITEMS[math.random(#SAMPLE_ITEMS)]
  local ok, sessionIdOrReason, extra = manager:OpenSession(link, 60)
  if not ok then
    if sessionIdOrReason == "version_gate" then
      DR:Print("Test session blocked by version gate: " .. table.concat(extra or {}, ", "))
    else
      DR:Print("Test session failed: " .. tostring(sessionIdOrReason))
    end
    return false
  end

  -- The raider frame auto-opens via the DR_SESSION_OPENED message.
  DR:Print("Test session opened with " .. link .. " (60s window).")
  return true
end

function TestMode:AddFakeSubmissions(count)
  if not self:IsActive() then
    DR:Print("Enable test mode first: /droll test")
    return
  end

  local manager = DR:Require("Services.SessionManager")
  if not manager:IsRaidLeaderAuthority() then
    DR:Print("Only the session authority can inject fake rolls.")
    return
  end

  local session = manager:GetLatestSession()
  if not session or session.state ~= constants.SESSION.STATE.COLLECTING then
    DR:Print("No collecting session. Start one with /droll test")
    return
  end

  count = math.min(math.max(tonumber(count) or 3, 1), 20)
  local identity = DR:Require("Services.Identity")
  local ledger = DR:Require("Services.Ledger")

  for _ = 1, count do
    self.fakeCounter = self.fakeCounter + 1
    local guid = FAKE_GUID_PREFIX .. self.fakeCounter
    identity:Bind(guid, "FakeRaider" .. self.fakeCounter)
    -- Tracked so fakes show up in the roster window while testing.
    ledger:Track(guid)

    local category = ROLLABLE_CATEGORIES[math.random(#ROLLABLE_CATEGORIES)]
    local baseRoll = manager:GenerateRoll(category)
    -- BuildSubmission pulls the fake raider's weight from the real ledger, so
    -- winner penalties and regains are exercised exactly like live players.
    local submit = manager:BuildSubmission(guid, category, baseRoll)
    manager:AcceptSubmission(session, submit, "test")
  end

  DR:Print(string.format("Injected %d fake submission(s).", count))
end

function TestMode:ResolveTestSession()
  local manager = DR:Require("Services.SessionManager")
  local session = manager:GetLatestSession()
  if not session then
    DR:Print("No session to resolve.")
    return
  end

  local ok, reason = manager:ResolveSession(session.id)
  if not ok then
    DR:Print("Resolve failed: " .. tostring(reason))
  end
end

function TestMode:HandleCommand(rest)
  local sub, tail = (rest or ""):match("^(%S*)%s*(.-)$")
  sub = string.lower(sub or "")

  if sub == "off" then
    self:Disable()
    return
  end

  if sub == "fake" then
    self:AddFakeSubmissions(tail)
    return
  end

  if sub == "resolve" then
    self:ResolveTestSession()
    return
  end

  if sub == "reward" then
    local manager = DR:Require("Services.SessionManager")
    local session = manager:GetLatestSession()
    if not session then
      DR:Print("No session to reward.")
      return
    end
    local ok, result = manager:RewardWinner(session.id, "test")
    if ok then
      DR:Print(string.format("Reward confirmed; winner's roll power is now %s%%.",
        DR:Require("Domain.RollEngine"):FormatWeight(result)))
    else
      DR:Print("Reward failed: " .. tostring(result))
    end
    return
  end

  if sub == "" or sub == "on" or sub == "start" then
    self:Enable()
    self:StartTestSession()
    return
  end

  DR:Print("Test mode usage: /droll test [on|off|fake N|resolve|reward]")
end

DR:RegisterModule("Modules.TestMode", TestMode)
