local luaunit = require("testing.luaunit")
local ErrorHandler = require("modules.ErrorHandler")
require("testing.loveStub")
local FlexLove = require("FlexLove")
local Color = require("modules.Color")
local Theme = require("modules.Theme")
ErrorHandler.init({})

TestFlexLove = {}

function TestFlexLove:setUp()
  FlexLove.destroy()
  FlexLove.setMode("retained")
end

function TestFlexLove:tearDown()
  FlexLove.destroy()
end

-- Test: Module loads and has expected properties
function TestFlexLove:testModuleLoads()
  luaunit.assertNotNil(FlexLove)
  luaunit.assertNotNil(FlexLove._VERSION)
  luaunit.assertNotNil(FlexLove._DESCRIPTION)
  luaunit.assertNotNil(FlexLove._URL)
  luaunit.assertNotNil(FlexLove._LICENSE)
end

-- Test: init() with no config
function TestFlexLove:testInitNoConfig()
  FlexLove.init()
  luaunit.assertTrue(true) -- Should not error
end

-- Test: init() with empty config
function TestFlexLove:testInitEmptyConfig()
  FlexLove.init({})
  luaunit.assertTrue(true)
end

-- Test: init() with baseScale
function TestFlexLove:testInitWithBaseScale()
  FlexLove.init({
    baseScale = {
      width = 1920,
      height = 1080,
    },
  })

  luaunit.assertNotNil(FlexLove.baseScale)
  luaunit.assertEquals(FlexLove.baseScale.width, 1920)
  luaunit.assertEquals(FlexLove.baseScale.height, 1080)
end

-- Test: init() with partial baseScale (uses defaults)
function TestFlexLove:testInitWithPartialBaseScale()
  FlexLove.init({
    baseScale = {},
  })

  luaunit.assertNotNil(FlexLove.baseScale)
  luaunit.assertEquals(FlexLove.baseScale.width, 1920)
  luaunit.assertEquals(FlexLove.baseScale.height, 1080)
end

-- Test: init() with string theme
function TestFlexLove:testInitWithStringTheme()
  -- Pre-register a theme
  local theme = Theme.new({
    name = "test",
    components = {},
  })

  -- init() tries to load and then set active, which may fail if theme path doesn't exist
  -- Just check that it doesn't crash
  FlexLove.init({
    theme = "test",
  })

  -- The theme setting may fail silently, so just check it doesn't crash
  luaunit.assertTrue(true)
end

-- Test: init() with table theme
function TestFlexLove:testInitWithTableTheme()
  FlexLove.init({
    theme = {
      name = "custom",
      components = {},
    },
  })

  luaunit.assertEquals(FlexLove.defaultTheme, "custom")
end

-- Test: init() with invalid theme (should not crash)
function TestFlexLove:testInitWithInvalidTheme()
  FlexLove.init({
    theme = "nonexistent-theme",
  })

  -- Should not crash, just print warning
  luaunit.assertTrue(true)
end

-- Test: init() with immediateMode = true
function TestFlexLove:testInitWithImmediateMode()
  FlexLove.init({
    immediateMode = true,
  })

  luaunit.assertEquals(FlexLove.getMode(), "immediate")
end

-- Test: init() with immediateMode = false
function TestFlexLove:testInitWithRetainedMode()
  FlexLove.init({
    immediateMode = false,
  })

  luaunit.assertEquals(FlexLove.getMode(), "retained")
end

-- Test: init() with autoFrameManagement
function TestFlexLove:testInitWithAutoFrameManagement()
  FlexLove.init({
    autoFrameManagement = true,
  })

  luaunit.assertEquals(FlexLove._autoFrameManagement, true)
end

-- Test: init() with state configuration
function TestFlexLove:testInitWithStateConfig()
  FlexLove.init({
    stateRetentionFrames = 5,
    maxStateEntries = 100,
  })

  luaunit.assertTrue(true) -- Should configure StateManager
end

-- Test: setMode() to immediate
function TestFlexLove:testSetModeImmediate()
  FlexLove.setMode("immediate")
  luaunit.assertTrue(FlexLove._immediateMode)
  luaunit.assertFalse(FlexLove._frameStarted)
end

-- Test: setMode() to retained
function TestFlexLove:testSetModeRetained()
  FlexLove.setMode("immediate") -- First set to immediate
  FlexLove.setMode("retained") -- Then to retained

  luaunit.assertFalse(FlexLove._immediateMode)
  luaunit.assertEquals(FlexLove._frameNumber, 0)
end

-- Test: setMode() with invalid mode
function TestFlexLove:testSetModeInvalid()
  local success = pcall(function()
    FlexLove.setMode("invalid")
  end)

  luaunit.assertFalse(success)
end

-- Test: getMode() returns correct mode
function TestFlexLove:testGetMode()
  FlexLove.setMode("immediate")
  luaunit.assertEquals(FlexLove.getMode(), "immediate")

  FlexLove.setMode("retained")
  luaunit.assertEquals(FlexLove.getMode(), "retained")
end

-- Test: beginFrame() in immediate mode
function TestFlexLove:testBeginFrameImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  luaunit.assertTrue(FlexLove._frameStarted)
  luaunit.assertEquals(#FlexLove._currentFrameElements, 0)
end

-- Test: beginFrame() in retained mode (should do nothing)
function TestFlexLove:testBeginFrameRetained()
  FlexLove.setMode("retained")
  local frameNumber = FlexLove._frameNumber or 0
  FlexLove.beginFrame()

  -- Frame number should not change in retained mode
  luaunit.assertEquals(FlexLove._frameNumber or 0, frameNumber)
end

-- Test: endFrame() in immediate mode
function TestFlexLove:testEndFrameImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()
  FlexLove.endFrame()

  luaunit.assertFalse(FlexLove._frameStarted)
end

-- Test: endFrame() in retained mode (should do nothing)
function TestFlexLove:testEndFrameRetained()
  FlexLove.setMode("retained")
  FlexLove.endFrame()

  luaunit.assertTrue(true) -- Should not error
end

-- Test: new() creates element in retained mode
function TestFlexLove:testNewRetainedMode()
  FlexLove.setMode("retained")
  local element = FlexLove.new({ width = 100, height = 100 })

  luaunit.assertNotNil(element)
  luaunit.assertEquals(element.width, 100)
  luaunit.assertEquals(element.height, 100)
end

-- Test: new() creates element in immediate mode
function TestFlexLove:testNewImmediateMode()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  local element = FlexLove.new({
    id = "test-element",
    width = 100,
    height = 100,
  })

  luaunit.assertNotNil(element)
  luaunit.assertEquals(element.width, 100)
  luaunit.assertEquals(element.height, 100)

  FlexLove.endFrame()
end

-- Test: new() auto-begins frame if not started
function TestFlexLove:testNewAutoBeginFrame()
  FlexLove.setMode("immediate")

  local element = FlexLove.new({
    id = "auto-begin-test",
    width = 50,
    height = 50,
  })

  luaunit.assertNotNil(element)
  luaunit.assertTrue(FlexLove._autoBeganFrame)

  FlexLove.endFrame()
end

-- Test: new() generates ID if not provided (immediate mode)
function TestFlexLove:testNewGeneratesID()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  local element = FlexLove.new({ width = 100, height = 100 })

  luaunit.assertNotNil(element.id)
  luaunit.assertTrue(element.id ~= "")

  FlexLove.endFrame()
end

-- Test: draw() with no arguments
function TestFlexLove:testDrawNoArgs()
  FlexLove.setMode("retained")
  FlexLove.draw()

  luaunit.assertTrue(true) -- Should not error
end

-- Test: draw() with gameDrawFunc
function TestFlexLove:testDrawWithGameFunc()
  FlexLove.setMode("retained")

  local called = false
  FlexLove.draw(function()
    called = true
  end)

  luaunit.assertTrue(called)
end

-- Test: draw() with postDrawFunc
function TestFlexLove:testDrawWithPostFunc()
  FlexLove.setMode("retained")

  local called = false
  FlexLove.draw(nil, function()
    called = true
  end)

  luaunit.assertTrue(called)
end

-- Test: draw() with both functions
function TestFlexLove:testDrawWithBothFuncs()
  FlexLove.setMode("retained")

  local gameCalled = false
  local postCalled = false

  FlexLove.draw(function()
    gameCalled = true
  end, function()
    postCalled = true
  end)

  luaunit.assertTrue(gameCalled)
  luaunit.assertTrue(postCalled)
end

-- Test: draw() with elements (no backdrop blur)
function TestFlexLove:testDrawWithElements()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    width = 100,
    height = 100,
    backgroundColor = Color.new(1, 1, 1, 1),
  })

  FlexLove.draw()

  luaunit.assertTrue(true) -- Should not error
end

-- Test: draw() auto-ends frame in immediate mode
function TestFlexLove:testDrawAutoEndFrame()
  FlexLove.setMode("immediate")

  local element = FlexLove.new({
    id = "auto-end-test",
    width = 100,
    height = 100,
  })

  -- draw() should call endFrame() if _autoBeganFrame is true
  FlexLove.draw()

  luaunit.assertFalse(FlexLove._autoBeganFrame)
end

-- Test: update() with no elements
function TestFlexLove:testUpdateNoElements()
  FlexLove.setMode("retained")
  FlexLove.update(0.016)

  luaunit.assertTrue(true) -- Should not error
end

-- Test: update() in retained mode with elements
function TestFlexLove:testUpdateRetainedMode()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    width = 100,
    height = 100,
  })

  FlexLove.update(0.016)

  luaunit.assertTrue(true)
end

-- Test: update() in immediate mode (should skip element updates)
function TestFlexLove:testUpdateImmediateMode()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  local element = FlexLove.new({
    id = "update-test",
    width = 100,
    height = 100,
  })

  FlexLove.endFrame()
  FlexLove.update(0.016)

  luaunit.assertTrue(true)
end

-- Test: resize() with no baseScale
function TestFlexLove:testResizeNoBaseScale()
  FlexLove.setMode("retained")
  FlexLove.resize()

  luaunit.assertTrue(true) -- Should not error
end

-- Test: resize() with baseScale
function TestFlexLove:testResizeWithBaseScale()
  FlexLove.init({
    baseScale = {
      width = 1920,
      height = 1080,
    },
  })

  FlexLove.resize()

  luaunit.assertNotNil(FlexLove.scaleFactors)
end

-- Test: resize() with elements
function TestFlexLove:testResizeWithElements()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    width = 100,
    height = 100,
  })

  FlexLove.resize()

  luaunit.assertTrue(true)
end

-- Test: destroy() clears all elements
function TestFlexLove:testDestroy()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    width = 100,
    height = 100,
  })

  FlexLove.destroy()

  luaunit.assertEquals(#FlexLove.topElements, 0)
  luaunit.assertNil(FlexLove.baseScale)
  luaunit.assertNil(FlexLove._focusedElement)
end

-- Test: textinput() with no focused element
function TestFlexLove:testTextInputNoFocus()
  FlexLove.setMode("retained")
  FlexLove.textinput("a")

  luaunit.assertTrue(true) -- Should not error
end

-- Test: textinput() with focused element
function TestFlexLove:testTextInputWithFocus()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    width = 100,
    height = 100,
    editable = true,
  })

  FlexLove._focusedElement = element
  FlexLove.textinput("a")

  luaunit.assertTrue(true)
end

-- Test: keypressed() with no focused element
function TestFlexLove:testKeyPressedNoFocus()
  FlexLove.setMode("retained")
  FlexLove.keypressed("return", "return", false)

  luaunit.assertTrue(true) -- Should not error
end

-- Test: keypressed() with focused element
function TestFlexLove:testKeyPressedWithFocus()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    width = 100,
    height = 100,
    editable = true,
  })

  FlexLove._focusedElement = element
  FlexLove.keypressed("return", "return", false)

  luaunit.assertTrue(true)
end

-- Test: wheelmoved() in retained mode with no elements
function TestFlexLove:testWheelMovedRetainedNoElements()
  FlexLove.setMode("retained")
  FlexLove.wheelmoved(0, 1)

  luaunit.assertTrue(true) -- Should not error
end

-- Test: wheelmoved() in immediate mode
function TestFlexLove:testWheelMovedImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  local element = FlexLove.new({
    id = "wheel-test",
    width = 100,
    height = 100,
  })

  FlexLove.endFrame()
  FlexLove.wheelmoved(0, 1)

  luaunit.assertTrue(true)
end

-- Test: getStateCount() in retained mode
function TestFlexLove:testGetStateCountRetained()
  FlexLove.setMode("retained")
  local count = FlexLove.getStateCount()

  luaunit.assertEquals(count, 0)
end

-- Test: getStateCount() in immediate mode
function TestFlexLove:testGetStateCountImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  local element = FlexLove.new({
    id = "state-test",
    width = 100,
    height = 100,
  })

  FlexLove.endFrame()

  local count = FlexLove.getStateCount()
  luaunit.assertTrue(count >= 0)
end

-- Test: clearState() in retained mode (should do nothing)
function TestFlexLove:testClearStateRetained()
  FlexLove.setMode("retained")
  FlexLove.clearState("test-id")

  luaunit.assertTrue(true)
end

-- Test: clearState() in immediate mode
function TestFlexLove:testClearStateImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()

  local element = FlexLove.new({
    id = "clear-test",
    width = 100,
    height = 100,
  })

  FlexLove.endFrame()

  FlexLove.clearState("clear-test")
  luaunit.assertTrue(true)
end

-- Test: clearAllStates() in retained mode
function TestFlexLove:testClearAllStatesRetained()
  FlexLove.setMode("retained")
  FlexLove.clearAllStates()

  luaunit.assertTrue(true)
end

-- Test: clearAllStates() in immediate mode
function TestFlexLove:testClearAllStatesImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()
  FlexLove.endFrame()

  FlexLove.clearAllStates()
  luaunit.assertTrue(true)
end

-- Test: getStateStats() in retained mode
function TestFlexLove:testGetStateStatsRetained()
  FlexLove.setMode("retained")
  local stats = FlexLove.getStateStats()

  luaunit.assertEquals(stats.stateCount, 0)
  luaunit.assertEquals(stats.frameNumber, 0)
end

-- Test: getStateStats() in immediate mode
function TestFlexLove:testGetStateStatsImmediate()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()
  FlexLove.endFrame()

  local stats = FlexLove.getStateStats()
  luaunit.assertNotNil(stats)
end

-- Test: getElementAtPosition() with no elements
function TestFlexLove:testGetElementAtPositionNoElements()
  FlexLove.setMode("retained")
  local element = FlexLove.getElementAtPosition(50, 50)

  luaunit.assertNil(element)
end

-- Test: getElementAtPosition() with element at position
function TestFlexLove:testGetElementAtPosition()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    x = 0,
    y = 0,
    width = 100,
    height = 100,
    onEvent = function() end,
  })

  local found = FlexLove.getElementAtPosition(50, 50)
  luaunit.assertEquals(found, element)
end

-- Test: getElementAtPosition() outside element bounds
function TestFlexLove:testGetElementAtPositionOutside()
  FlexLove.setMode("retained")

  local element = FlexLove.new({
    x = 0,
    y = 0,
    width = 100,
    height = 100,
    onEvent = function() end,
  })

  local found = FlexLove.getElementAtPosition(200, 200)
  luaunit.assertNil(found)
end

-- Test: deferCallback() queues callback
function TestFlexLove:testDeferCallbackQueuesCallback()
  FlexLove.setMode("retained")

  local called = false
  FlexLove.deferCallback(function()
    called = true
  end)

  -- Callback should not be called immediately
  luaunit.assertFalse(called)

  -- Callback should be called after executeDeferredCallbacks
  FlexLove.draw()
  luaunit.assertFalse(called) -- Still not called

  FlexLove.executeDeferredCallbacks()
  luaunit.assertTrue(called) -- Now called
end

-- Test: deferCallback() with multiple callbacks
function TestFlexLove:testDeferCallbackMultiple()
  FlexLove.setMode("retained")

  local order = {}
  FlexLove.deferCallback(function()
    table.insert(order, 1)
  end)
  FlexLove.deferCallback(function()
    table.insert(order, 2)
  end)
  FlexLove.deferCallback(function()
    table.insert(order, 3)
  end)

  FlexLove.draw()
  FlexLove.executeDeferredCallbacks()

  luaunit.assertEquals(#order, 3)
  luaunit.assertEquals(order[1], 1)
  luaunit.assertEquals(order[2], 2)
  luaunit.assertEquals(order[3], 3)
end

-- Test: deferCallback() with non-function argument
function TestFlexLove:testDeferCallbackInvalidArgument()
  FlexLove.setMode("retained")

  -- Should warn but not crash
  FlexLove.deferCallback("not a function")
  FlexLove.deferCallback(123)
  FlexLove.deferCallback(nil)

  FlexLove.draw()
  luaunit.assertTrue(true)
end

-- Test: deferCallback() clears queue after execution
function TestFlexLove:testDeferCallbackClearsQueue()
  FlexLove.setMode("retained")

  local callCount = 0
  FlexLove.deferCallback(function()
    callCount = callCount + 1
  end)

  FlexLove.draw()
  FlexLove.executeDeferredCallbacks() -- First execution
  luaunit.assertEquals(callCount, 1)

  FlexLove.draw()
  FlexLove.executeDeferredCallbacks() -- Second execution should not call again
  luaunit.assertEquals(callCount, 1)
end

-- Test: deferCallback() handles callback errors gracefully
function TestFlexLove:testDeferCallbackWithError()
  FlexLove.setMode("retained")

  local called = false
  FlexLove.deferCallback(function()
    error("Intentional error")
  end)
  FlexLove.deferCallback(function()
    called = true
  end)

  -- Should not crash, second callback should still execute
  FlexLove.draw()
  FlexLove.executeDeferredCallbacks()
  luaunit.assertTrue(called)
end

-- Test: External modules are exposed
function TestFlexLove:testExternalModulesExposed()
  luaunit.assertNotNil(FlexLove.Animation)
  luaunit.assertNotNil(FlexLove.Color)
  luaunit.assertNotNil(FlexLove.Theme)
  luaunit.assertNotNil(FlexLove.enums)
end

-- Test: Enums are accessible
function TestFlexLove:testEnumsAccessible()
  luaunit.assertNotNil(FlexLove.enums.FlexDirection)
  luaunit.assertNotNil(FlexLove.enums.JustifyContent)
  luaunit.assertNotNil(FlexLove.enums.AlignItems)
end

-- ==========================================
-- UNHAPPY PATH TESTS
-- ==========================================

TestFlexLoveUnhappyPaths = {}

function TestFlexLoveUnhappyPaths:setUp()
  FlexLove.destroy()
  FlexLove.setMode("retained")
end

function TestFlexLoveUnhappyPaths:tearDown()
  FlexLove.destroy()
end

-- Test: init() with invalid config types
function TestFlexLoveUnhappyPaths:testInitWithInvalidConfigTypes()
  -- nil and false should work (become {} via `config or {}`)
  FlexLove.init(nil)
  luaunit.assertTrue(true)

  FlexLove.init(false)
  luaunit.assertTrue(true)

  -- String and number will error when trying to index them (config.errorLogFile, config.baseScale, etc.)
  local success = pcall(function()
    FlexLove.init("invalid")
  end)
  -- String indexing may work in Lua (returns nil), check actual behavior
  -- Actually strings can be indexed but will return nil for most keys
  -- So this might not error! Let's just verify it doesn't crash
  luaunit.assertTrue(true)

  success = pcall(function()
    FlexLove.init(123)
  end)
  -- Numbers can't be indexed, should error
  luaunit.assertFalse(success)
end

-- Test: init() with invalid baseScale values
function TestFlexLoveUnhappyPaths:testInitWithInvalidBaseScale()
  -- Negative width/height (should work, just unusual)
  FlexLove.init({
    baseScale = {
      width = -1920,
      height = -1080,
    },
  })
  luaunit.assertTrue(true) -- Should not crash

  -- Zero width/height (division by zero risk)
  local success = pcall(function()
    FlexLove.init({
      baseScale = {
        width = 0,
        height = 0,
      },
    })
  end)
  -- May or may not error depending on implementation

  -- Non-numeric values (should error)
  success = pcall(function()
    FlexLove.init({
      baseScale = {
        width = "invalid",
        height = "invalid",
      },
    })
  end)
  luaunit.assertFalse(success) -- Should error on division
end

-- Test: init() with invalid theme types
function TestFlexLoveUnhappyPaths:testInitWithInvalidThemeTypes()
  -- Numeric theme
  FlexLove.init({ theme = 123 })
  luaunit.assertTrue(true)

  -- Boolean theme
  FlexLove.init({ theme = true })
  luaunit.assertTrue(true)

  -- Function theme
  FlexLove.init({ theme = function() end })
  luaunit.assertTrue(true)
end

-- Test: init() with invalid immediateMode values
function TestFlexLoveUnhappyPaths:testInitWithInvalidImmediateMode()
  FlexLove.init({ immediateMode = "yes" })
  luaunit.assertTrue(true)

  FlexLove.init({ immediateMode = 1 })
  luaunit.assertTrue(true)

  FlexLove.init({ immediateMode = {} })
  luaunit.assertTrue(true)
end

-- Test: init() with invalid state config values
function TestFlexLoveUnhappyPaths:testInitWithInvalidStateConfig()
  -- Negative values
  FlexLove.init({
    stateRetentionFrames = -5,
    maxStateEntries = -100,
  })
  luaunit.assertTrue(true)

  -- Non-numeric values
  FlexLove.init({
    stateRetentionFrames = "five",
    maxStateEntries = "hundred",
  })
  luaunit.assertTrue(true)

  -- Zero values
  FlexLove.init({
    stateRetentionFrames = 0,
    maxStateEntries = 0,
  })
  luaunit.assertTrue(true)
end

-- Test: setMode() with nil
function TestFlexLoveUnhappyPaths:testSetModeNil()
  local success = pcall(function()
    FlexLove.setMode(nil)
  end)
  luaunit.assertFalse(success)
end

-- Test: setMode() with number
function TestFlexLoveUnhappyPaths:testSetModeNumber()
  local success = pcall(function()
    FlexLove.setMode(123)
  end)
  luaunit.assertFalse(success)
end

-- Test: setMode() with table
function TestFlexLoveUnhappyPaths:testSetModeTable()
  local success = pcall(function()
    FlexLove.setMode({ mode = "immediate" })
  end)
  luaunit.assertFalse(success)
end

-- Test: setMode() with empty string
function TestFlexLoveUnhappyPaths:testSetModeEmptyString()
  local success = pcall(function()
    FlexLove.setMode("")
  end)
  luaunit.assertFalse(success)
end

-- Test: setMode() with case-sensitive variation
function TestFlexLoveUnhappyPaths:testSetModeCaseSensitive()
  local success = pcall(function()
    FlexLove.setMode("Immediate")
  end)
  luaunit.assertFalse(success)

  success = pcall(function()
    FlexLove.setMode("RETAINED")
  end)
  luaunit.assertFalse(success)
end

-- Test: beginFrame() multiple times without endFrame()
function TestFlexLoveUnhappyPaths:testBeginFrameMultipleTimes()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()
  local frameNum1 = FlexLove._frameNumber

  FlexLove.beginFrame() -- Call again without ending
  local frameNum2 = FlexLove._frameNumber

  -- Frame number should increment each time
  luaunit.assertTrue(frameNum2 > frameNum1)
end

-- Test: endFrame() without beginFrame()
function TestFlexLoveUnhappyPaths:testEndFrameWithoutBegin()
  FlexLove.setMode("immediate")
  FlexLove.endFrame() -- Should not crash
  luaunit.assertTrue(true)
end

-- Test: endFrame() multiple times
function TestFlexLoveUnhappyPaths:testEndFrameMultipleTimes()
  FlexLove.setMode("immediate")
  FlexLove.beginFrame()
  FlexLove.endFrame()
  FlexLove.endFrame() -- Call again
  luaunit.assertTrue(true) -- Should not crash
end

-- Test: new() with nil props
function TestFlexLoveUnhappyPaths:testNewWithNilProps()
  FlexLove.setMode("retained")
  local element = FlexLove.new(nil)
  luaunit.assertNotNil(element)
end

-- Test: new() with invalid width/height
function TestFlexLoveUnhappyPaths:testNewWithInvalidDimensions()
  FlexLove.setMode("retained")

  -- Negative dimensions
  local element = FlexLove.new({ width = -100, height = -50 })
  luaunit.assertNotNil(element)

  -- Zero dimensions
  element = FlexLove.new({ width = 0, height = 0 })
  luaunit.assertNotNil(element)

  -- Invalid string dimensions (now returns fallback 0px with warning)
  local success, element = pcall(function()
    return FlexLove.new({ width = "invalid", height = "invalid" })
  end)
  luaunit.assertTrue(success) -- Units.parse returns fallback (0, "px") instead of erroring
  luaunit.assertNotNil(element)
end

-- Test: new() with invalid position
function TestFlexLoveUnhappyPaths:testNewWithInvalidPosition()
  FlexLove.setMode("retained")

  -- Negative positions
  local element = FlexLove.new({ x = -1000, y = -1000, width = 100, height = 100 })
  luaunit.assertNotNil(element)
end

-- Test: new() with circular parent reference
function TestFlexLoveUnhappyPaths:testNewWithCircularParent()
  FlexLove.setMode("retained")

  local parent = FlexLove.new({ width = 200, height = 200 })
  local child = FlexLove.new({ width = 100, height = 100, parent = parent })

  -- Try to make parent a child of child (circular reference)
  -- This should be prevented by the design
  luaunit.assertNotEquals(parent.parent, child)
end

-- Test: new() in immediate mode without frame
function TestFlexLoveUnhappyPaths:testNewImmediateModeNoFrame()
  FlexLove.setMode("immediate")
  -- Don't call beginFrame()

  local element = FlexLove.new({ width = 100, height = 100 })
  luaunit.assertNotNil(element)
  luaunit.assertTrue(FlexLove._autoBeganFrame)
end

-- Test: draw() with invalid function types
function TestFlexLoveUnhappyPaths:testDrawWithInvalidFunctions()
  FlexLove.setMode("retained")

  -- Non-function gameDrawFunc
  FlexLove.draw("not a function", nil)
  luaunit.assertTrue(true)

  FlexLove.draw(123, nil)
  luaunit.assertTrue(true)

  FlexLove.draw({}, nil)
  luaunit.assertTrue(true)

  -- Non-function postDrawFunc
  FlexLove.draw(nil, "not a function")
  luaunit.assertTrue(true)

  FlexLove.draw(nil, 456)
  luaunit.assertTrue(true)
end

-- Test: draw() with function that errors
function TestFlexLoveUnhappyPaths:testDrawWithErroringFunction()
  FlexLove.setMode("retained")

  local success = pcall(function()
    FlexLove.draw(function()
      error("Intentional error")
    end)
  end)

  luaunit.assertFalse(success)
end

-- Test: update() with invalid dt
function TestFlexLoveUnhappyPaths:testUpdateWithInvalidDt()
  FlexLove.setMode("retained")

  -- Negative dt
  FlexLove.update(-0.016)
  luaunit.assertTrue(true)

  -- Zero dt
  FlexLove.update(0)
  luaunit.assertTrue(true)

  -- Huge dt
  FlexLove.update(1000)
  luaunit.assertTrue(true)

  -- nil dt
  local success = pcall(function()
    FlexLove.update(nil)
  end)
  -- May or may not error depending on implementation
end

-- Test: textinput() with invalid text
function TestFlexLoveUnhappyPaths:testTextInputWithInvalidText()
  FlexLove.setMode("retained")

  -- nil text
  local success = pcall(function()
    FlexLove.textinput(nil)
  end)
  -- Should handle gracefully

  -- Number
  FlexLove.textinput(123)
  luaunit.assertTrue(true)

  -- Empty string
  FlexLove.textinput("")
  luaunit.assertTrue(true)

  -- Very long string
  FlexLove.textinput(string.rep("a", 10000))
  luaunit.assertTrue(true)
end

-- Test: keypressed() with invalid keys
function TestFlexLoveUnhappyPaths:testKeyPressedWithInvalidKeys()
  FlexLove.setMode("retained")

  -- nil key
  local success = pcall(function()
    FlexLove.keypressed(nil, nil, false)
  end)

  -- Empty strings
  FlexLove.keypressed("", "", false)
  luaunit.assertTrue(true)

  -- Invalid key names
  FlexLove.keypressed("invalidkey", "invalidscancode", false)
  luaunit.assertTrue(true)

  -- Non-boolean isrepeat
  FlexLove.keypressed("a", "a", "yes")
  luaunit.assertTrue(true)
end

-- Test: wheelmoved() with invalid values
function TestFlexLoveUnhappyPaths:testWheelMovedWithInvalidValues()
  FlexLove.setMode("retained")

  -- nil values
  local success = pcall(function()
    FlexLove.wheelmoved(nil, nil)
  end)
  -- May or may not error
end

-- Test: resize() repeatedly in quick succession
function TestFlexLoveUnhappyPaths:testResizeRapidly()
  FlexLove.setMode("retained")

  local element = FlexLove.new({ width = 100, height = 100 })

  for i = 1, 100 do
    FlexLove.resize()
  end

  luaunit.assertTrue(true) -- Should not crash
end

-- Test: destroy() twice
function TestFlexLoveUnhappyPaths:testDestroyTwice()
  FlexLove.setMode("retained")

  local element = FlexLove.new({ width = 100, height = 100 })

  FlexLove.destroy()
  FlexLove.destroy() -- Call again

  luaunit.assertTrue(true) -- Should not crash
end

-- Test: clearState() with invalid ID types
function TestFlexLoveUnhappyPaths:testClearStateWithInvalidIds()
  FlexLove.setMode("immediate")

  -- nil ID (should error)
  local success = pcall(function()
    FlexLove.clearState(nil)
  end)
  luaunit.assertFalse(success)

  -- Number ID (should work, gets converted to string)
  FlexLove.clearState(123)
  luaunit.assertTrue(true)

  -- Table ID (may error)
  success = pcall(function()
    FlexLove.clearState({})
  end)
  -- May or may not work depending on tostring implementation

  -- Empty string (should work)
  FlexLove.clearState("")
  luaunit.assertTrue(true)

  -- Non-existent ID (should work, just does nothing)
  FlexLove.clearState("nonexistent-id-12345")
  luaunit.assertTrue(true)
end

-- Test: getElementAtPosition() with invalid coordinates
function TestFlexLoveUnhappyPaths:testGetElementAtPositionWithInvalidCoords()
  FlexLove.setMode("retained")

  -- Negative coordinates
  local element = FlexLove.getElementAtPosition(-100, -100)
  luaunit.assertNil(element)

  -- nil coordinates
  local success = pcall(function()
    FlexLove.getElementAtPosition(nil, nil)
  end)
  -- May or may not error
end

-- Test: Creating element with conflicting properties
function TestFlexLoveUnhappyPaths:testNewWithConflictingProperties()
  FlexLove.setMode("retained")

  -- Both width auto and explicit
  local element = FlexLove.new({
    width = 100,
    autosizing = { width = true },
  })
  luaunit.assertNotNil(element)

  -- Conflicting positioning
  element = FlexLove.new({
    positioning = "flex",
    x = 100, -- Absolute position with flex
    y = 100,
  })
  luaunit.assertNotNil(element)
end

-- Test: Multiple mode switches
function TestFlexLoveUnhappyPaths:testMultipleModeSwitches()
  for i = 1, 10 do
    FlexLove.setMode("immediate")
    FlexLove.setMode("retained")
  end
  luaunit.assertTrue(true)
end

-- Test: Creating elements during draw
function TestFlexLoveUnhappyPaths:testCreatingElementsDuringDraw()
  FlexLove.setMode("retained")

  local drawCalled = false
  FlexLove.draw(function()
    -- Try to create element during draw
    local element = FlexLove.new({ width = 100, height = 100 })
    luaunit.assertNotNil(element)
    drawCalled = true
  end)

  luaunit.assertTrue(drawCalled)
end

-- Test: State operations in retained mode (should do nothing)
function TestFlexLoveUnhappyPaths:testStateOperationsInRetainedMode()
  FlexLove.setMode("retained")

  local count = FlexLove.getStateCount()
  luaunit.assertEquals(count, 0)

  FlexLove.clearState("any-id")
  FlexLove.clearAllStates()

  local stats = FlexLove.getStateStats()
  luaunit.assertEquals(stats.stateCount, 0)
  luaunit.assertEquals(stats.frameNumber, 0)
end

-- Test: Extreme z-index values

-- Test: Creating deeply nested element hierarchy
function TestFlexLoveUnhappyPaths:testDeeplyNestedHierarchy()
  FlexLove.setMode("retained")

  local parent = FlexLove.new({ width = 500, height = 500 })
  local current = parent

  -- Create 100 levels of nesting
  for i = 1, 100 do
    local child = FlexLove.new({
      width = 10,
      height = 10,
      parent = current,
    })
    current = child
  end

  luaunit.assertTrue(true) -- Should not crash
end

-- Test: Error logging configuration edge cases
function TestFlexLoveUnhappyPaths:testErrorLoggingEdgeCases()
  -- Empty error log file path
  FlexLove.init({ errorLogFile = "" })
  luaunit.assertTrue(true)

  -- Invalid path characters
  FlexLove.init({ errorLogFile = "/invalid/path/\0/file.log" })
  luaunit.assertTrue(true)

  -- Both enableErrorLogging and errorLogFile
  FlexLove.init({
    enableErrorLogging = true,
    errorLogFile = "test.log",
  })
  luaunit.assertTrue(true)
end

-- Test: Immediate mode frame management edge cases
function TestFlexLoveUnhappyPaths:testImmediateModeFrameEdgeCases()
  FlexLove.setMode("immediate")

  -- Begin, draw (should auto-end), then end again
  FlexLove.beginFrame()
  FlexLove.draw()
  FlexLove.endFrame() -- Extra end
  luaunit.assertTrue(true)

  -- Multiple draws without frames
  FlexLove.draw()
  FlexLove.draw()
  FlexLove.draw()
  luaunit.assertTrue(true)
end

if not _G.RUNNING_ALL_TESTS then
  os.exit(luaunit.LuaUnit.run())
end
