-- Test suite for utils.lua module
-- Tests all 16+ utility functions with comprehensive edge cases

package.path = package.path .. ";./?.lua;./modules/?.lua"

-- Load love stub before anything else
require("testing.loveStub")

local luaunit = require("testing.luaunit")
local ErrorHandler = require("modules.ErrorHandler")

-- Initialize ErrorHandler
ErrorHandler.init({})
local utils = require("modules.utils")
local ErrorHandler = require("modules.ErrorHandler")

-- Initialize ErrorHandler
ErrorHandler.init({})

-- Test suite for validation utilities
TestValidationUtils = {}

function TestValidationUtils:testValidateEnum_ValidValue()
  local testEnum = { VALUE1 = "value1", VALUE2 = "value2", VALUE3 = "value3" }
  luaunit.assertTrue(utils.validateEnum("value1", testEnum, "testProp"))
  luaunit.assertTrue(utils.validateEnum("value2", testEnum, "testProp"))
  luaunit.assertTrue(utils.validateEnum("value3", testEnum, "testProp"))
end

function TestValidationUtils:testValidateEnum_NilValue()
  local testEnum = { VALUE1 = "value1", VALUE2 = "value2" }
  luaunit.assertTrue(utils.validateEnum(nil, testEnum, "testProp"))
end

function TestValidationUtils:testValidateEnum_InvalidValue()
  local testEnum = { VALUE1 = "value1", VALUE2 = "value2" }
  luaunit.assertErrorMsgContains("VAL_007", function()
    utils.validateEnum("invalid", testEnum, "testProp")
  end)
end

function TestValidationUtils:testValidateRange_InRange()
  luaunit.assertTrue(utils.validateRange(5, 0, 10, "testProp"))
  luaunit.assertTrue(utils.validateRange(0, 0, 10, "testProp"))
  luaunit.assertTrue(utils.validateRange(10, 0, 10, "testProp"))
end

function TestValidationUtils:testValidateRange_OutOfRange()
  luaunit.assertErrorMsgContains("VAL_002", function()
    utils.validateRange(-1, 0, 10, "testProp")
  end)
  luaunit.assertErrorMsgContains("VAL_002", function()
    utils.validateRange(11, 0, 10, "testProp")
  end)
end

function TestValidationUtils:testValidateRange_NilValue()
  luaunit.assertTrue(utils.validateRange(nil, 0, 10, "testProp"))
end

function TestValidationUtils:testValidateRange_WrongType()
  luaunit.assertErrorMsgContains("VAL_001", function()
    utils.validateRange("not a number", 0, 10, "testProp")
  end)
end

function TestValidationUtils:testValidateType_CorrectType()
  luaunit.assertTrue(utils.validateType("hello", "string", "testProp"))
  luaunit.assertTrue(utils.validateType(123, "number", "testProp"))
  luaunit.assertTrue(utils.validateType(true, "boolean", "testProp"))
  luaunit.assertTrue(utils.validateType({}, "table", "testProp"))
  luaunit.assertTrue(utils.validateType(function() end, "function", "testProp"))
end

function TestValidationUtils:testValidateType_WrongType()
  luaunit.assertErrorMsgContains("VAL_001", function()
    utils.validateType(123, "string", "testProp")
  end)
  luaunit.assertErrorMsgContains("VAL_001", function()
    utils.validateType("hello", "number", "testProp")
  end)
end

function TestValidationUtils:testValidateType_NilValue()
  luaunit.assertTrue(utils.validateType(nil, "string", "testProp"))
end

-- Test suite for math utilities
TestMathUtils = {}

function TestMathUtils:testClamp_WithinRange()
  luaunit.assertEquals(utils.clamp(5, 0, 10), 5)
  luaunit.assertEquals(utils.clamp(0, 0, 10), 0)
  luaunit.assertEquals(utils.clamp(10, 0, 10), 10)
end

function TestMathUtils:testClamp_BelowMin()
  luaunit.assertEquals(utils.clamp(-5, 0, 10), 0)
  luaunit.assertEquals(utils.clamp(-100, 0, 10), 0)
end

function TestMathUtils:testClamp_AboveMax()
  luaunit.assertEquals(utils.clamp(15, 0, 10), 10)
  luaunit.assertEquals(utils.clamp(100, 0, 10), 10)
end

function TestMathUtils:testClamp_NegativeRange()
  luaunit.assertEquals(utils.clamp(-5, -10, -1), -5)
  luaunit.assertEquals(utils.clamp(-15, -10, -1), -10)
  luaunit.assertEquals(utils.clamp(0, -10, -1), -1)
end

function TestMathUtils:testLerp_Boundaries()
  luaunit.assertEquals(utils.lerp(0, 10, 0), 0)
  luaunit.assertEquals(utils.lerp(0, 10, 1), 10)
end

function TestMathUtils:testLerp_Midpoint()
  luaunit.assertEquals(utils.lerp(0, 10, 0.5), 5)
  luaunit.assertEquals(utils.lerp(10, 20, 0.5), 15)
end

function TestMathUtils:testLerp_NegativeValues()
  luaunit.assertEquals(utils.lerp(-10, 10, 0.5), 0)
  luaunit.assertEquals(utils.lerp(-20, -10, 0.5), -15)
end

function TestMathUtils:testLerp_BeyondRange()
  luaunit.assertEquals(utils.lerp(0, 10, 1.5), 15)
  luaunit.assertEquals(utils.lerp(0, 10, -0.5), -5)
end

function TestMathUtils:testRound_UpAndDown()
  luaunit.assertEquals(utils.round(0.4), 0)
  luaunit.assertEquals(utils.round(0.5), 1)
  luaunit.assertEquals(utils.round(0.6), 1)
end

function TestMathUtils:testRound_NegativeNumbers()
  luaunit.assertEquals(utils.round(-0.4), 0)
  luaunit.assertEquals(utils.round(-0.5), 0)
  luaunit.assertEquals(utils.round(-0.6), -1)
end

function TestMathUtils:testRound_WholeNumbers()
  luaunit.assertEquals(utils.round(5), 5)
  luaunit.assertEquals(utils.round(-5), -5)
  luaunit.assertEquals(utils.round(0), 0)
end

-- Test suite for path utilities
TestPathUtils = {}

function TestPathUtils:testNormalizePath_Whitespace()
  luaunit.assertEquals(utils.normalizePath("  /path/to/file  "), "/path/to/file")
  luaunit.assertEquals(utils.normalizePath("\t/path/to/file\t"), "/path/to/file")
  luaunit.assertEquals(utils.normalizePath(" /path/to/file "), "/path/to/file")
end

function TestPathUtils:testNormalizePath_Backslashes()
  luaunit.assertEquals(utils.normalizePath("C:\\path\\to\\file"), "C:/path/to/file")
  luaunit.assertEquals(utils.normalizePath("path\\to\\file"), "path/to/file")
end

function TestPathUtils:testNormalizePath_DuplicateSlashes()
  luaunit.assertEquals(utils.normalizePath("/path//to///file"), "/path/to/file")
  luaunit.assertEquals(utils.normalizePath("path//to//file"), "path/to/file")
end

function TestPathUtils:testNormalizePath_Combined()
  luaunit.assertEquals(utils.normalizePath("  C:\\path\\\\to///file  "), "C:/path/to/file")
end

function TestPathUtils:testResolveImagePath_AbsolutePath()
  local result = utils.resolveImagePath("/absolute/path/to/image.png")
  luaunit.assertEquals(result, "/absolute/path/to/image.png")
end

function TestPathUtils:testResolveImagePath_WindowsAbsolutePath()
  local result = utils.resolveImagePath("C:/path/to/image.png")
  luaunit.assertEquals(result, "C:/path/to/image.png")
end

function TestPathUtils:testResolveImagePath_RelativePath()
  local result = utils.resolveImagePath("themes/images/icon.png")
  -- Should prepend the FlexLove base path
  luaunit.assertStrContains(result, "themes/images/icon.png")
end

function TestPathUtils:testSafeLoadImage_InvalidPath()
  -- Test with an invalid path - should return nil and error message
  local image, imageData, errorMsg = utils.safeLoadImage("nonexistent/path/to/image.png")
  luaunit.assertNil(image)
  luaunit.assertNil(imageData)
  luaunit.assertNotNil(errorMsg)
  luaunit.assertStrContains(errorMsg, "Failed to load")
end

-- Test suite for color utilities
TestColorUtils = {}

function TestColorUtils:testBrightenColor_NormalFactor()
  local r, g, b, a = utils.brightenColor(0.5, 0.5, 0.5, 1.0, 1.5)
  luaunit.assertAlmostEquals(r, 0.75, 0.001)
  luaunit.assertAlmostEquals(g, 0.75, 0.001)
  luaunit.assertAlmostEquals(b, 0.75, 0.001)
  luaunit.assertAlmostEquals(a, 1.0, 0.001)
end

function TestColorUtils:testBrightenColor_ClampingAt1()
  local r, g, b, a = utils.brightenColor(0.8, 0.8, 0.8, 1.0, 2.0)
  luaunit.assertAlmostEquals(r, 1.0, 0.001)
  luaunit.assertAlmostEquals(g, 1.0, 0.001)
  luaunit.assertAlmostEquals(b, 1.0, 0.001)
  luaunit.assertAlmostEquals(a, 1.0, 0.001)
end

function TestColorUtils:testBrightenColor_FactorOne()
  local r, g, b, a = utils.brightenColor(0.5, 0.6, 0.7, 0.8, 1.0)
  luaunit.assertAlmostEquals(r, 0.5, 0.001)
  luaunit.assertAlmostEquals(g, 0.6, 0.001)
  luaunit.assertAlmostEquals(b, 0.7, 0.001)
  luaunit.assertAlmostEquals(a, 0.8, 0.001)
end

function TestColorUtils:testBrightenColor_AlphaUnchanged()
  local _, _, _, a = utils.brightenColor(0.5, 0.5, 0.5, 0.5, 2.0)
  luaunit.assertAlmostEquals(a, 0.5, 0.001) -- Alpha should remain unchanged
end

-- Test suite for property utilities
TestPropertyUtils = {}

function TestPropertyUtils:testNormalizeBooleanTable_Boolean()
  local result = utils.normalizeBooleanTable(true)
  luaunit.assertEquals(result.vertical, true)
  luaunit.assertEquals(result.horizontal, true)

  result = utils.normalizeBooleanTable(false)
  luaunit.assertEquals(result.vertical, false)
  luaunit.assertEquals(result.horizontal, false)
end

function TestPropertyUtils:testNormalizeBooleanTable_Nil()
  local result = utils.normalizeBooleanTable(nil)
  luaunit.assertEquals(result.vertical, false)
  luaunit.assertEquals(result.horizontal, false)
end

function TestPropertyUtils:testNormalizeBooleanTable_NilWithDefault()
  local result = utils.normalizeBooleanTable(nil, true)
  luaunit.assertEquals(result.vertical, true)
  luaunit.assertEquals(result.horizontal, true)
end

function TestPropertyUtils:testNormalizeBooleanTable_Table()
  local result = utils.normalizeBooleanTable({ vertical = true, horizontal = false })
  luaunit.assertEquals(result.vertical, true)
  luaunit.assertEquals(result.horizontal, false)
end

function TestPropertyUtils:testNormalizeBooleanTable_PartialTable()
  local result = utils.normalizeBooleanTable({ vertical = true })
  luaunit.assertEquals(result.vertical, true)
  luaunit.assertEquals(result.horizontal, false)

  result = utils.normalizeBooleanTable({ horizontal = true })
  luaunit.assertEquals(result.vertical, false)
  luaunit.assertEquals(result.horizontal, true)
end

function TestPropertyUtils:testNormalizeBooleanTable_EmptyTable()
  local result = utils.normalizeBooleanTable({})
  luaunit.assertEquals(result.vertical, false)
  luaunit.assertEquals(result.horizontal, false)
end

-- Test suite for font utilities
TestFontUtils = {}

function TestFontUtils:testResolveFontPath_DirectPath()
  local result = utils.resolveFontPath("path/to/font.ttf", nil, nil)
  luaunit.assertEquals(result, "path/to/font.ttf")
end

function TestFontUtils:testResolveFontPath_NilFontFamily()
  local result = utils.resolveFontPath(nil, nil, nil)
  luaunit.assertNil(result)
end

function TestFontUtils:testResolveFontPath_ThemeFont()
  -- Mock theme manager with font
  local mockThemeManager = {
    getTheme = function()
      return {
        fonts = {
          mainFont = "themes/fonts/main.ttf",
        },
      }
    end,
  }

  local result = utils.resolveFontPath("mainFont", "button", mockThemeManager)
  luaunit.assertEquals(result, "themes/fonts/main.ttf")
end

function TestFontUtils:testResolveFontPath_ThemeFontNotFound()
  -- Mock theme manager without the requested font
  local mockThemeManager = {
    getTheme = function()
      return {
        fonts = {},
      }
    end,
  }

  -- Should fall back to treating it as a direct path
  local result = utils.resolveFontPath("unknownFont", "button", mockThemeManager)
  luaunit.assertEquals(result, "unknownFont")
end

function TestFontUtils:testGetFont_WithTextSize()
  local font = utils.getFont(16, nil, nil, nil)
  luaunit.assertNotNil(font)
  -- Font height should match the requested size
  if font.getHeight then
    luaunit.assertEquals(font.getHeight(), 16)
  end
end

function TestFontUtils:testGetFont_WithoutTextSize()
  local font = utils.getFont(nil, nil, nil, nil)
  luaunit.assertNotNil(font)
  -- Should return the default font
end

function TestFontUtils:testApplyContentMultiplier_WithWidth()
  local result = utils.applyContentMultiplier(100, { width = 2.0, height = 1.5 }, "width")
  luaunit.assertEquals(result, 200)
end

function TestFontUtils:testApplyContentMultiplier_WithHeight()
  local result = utils.applyContentMultiplier(100, { width = 2.0, height = 1.5 }, "height")
  luaunit.assertEquals(result, 150)
end

function TestFontUtils:testApplyContentMultiplier_NilMultiplier()
  local result = utils.applyContentMultiplier(100, nil, "width")
  luaunit.assertEquals(result, 100)
end

function TestFontUtils:testApplyContentMultiplier_MissingAxis()
  local result = utils.applyContentMultiplier(100, { width = 2.0 }, "height")
  luaunit.assertEquals(result, 100)
end

-- Test suite for input utilities
TestInputUtils = {}

function TestInputUtils:testGetModifiers_NoModifiers()
  -- Reset all modifier keys
  love.keyboard.setDown("lshift", false)
  love.keyboard.setDown("rshift", false)
  love.keyboard.setDown("lctrl", false)
  love.keyboard.setDown("rctrl", false)
  love.keyboard.setDown("lalt", false)
  love.keyboard.setDown("ralt", false)
  love.keyboard.setDown("lgui", false)
  love.keyboard.setDown("rgui", false)

  local mods = utils.getModifiers()
  luaunit.assertFalse(mods.shift)
  luaunit.assertFalse(mods.ctrl)
  luaunit.assertFalse(mods.alt)
  luaunit.assertFalse(mods.super)
end

function TestInputUtils:testGetModifiers_ShiftKey()
  love.keyboard.setDown("lshift", true)
  local mods = utils.getModifiers()
  luaunit.assertTrue(mods.shift)
  love.keyboard.setDown("lshift", false)

  love.keyboard.setDown("rshift", true)
  mods = utils.getModifiers()
  luaunit.assertTrue(mods.shift)
  love.keyboard.setDown("rshift", false)
end

function TestInputUtils:testGetModifiers_CtrlKey()
  love.keyboard.setDown("lctrl", true)
  local mods = utils.getModifiers()
  luaunit.assertTrue(mods.ctrl)
  love.keyboard.setDown("lctrl", false)
end

function TestInputUtils:testGetModifiers_AltKey()
  love.keyboard.setDown("lalt", true)
  local mods = utils.getModifiers()
  luaunit.assertTrue(mods.alt)
  love.keyboard.setDown("lalt", false)
end

function TestInputUtils:testGetModifiers_SuperKey()
  love.keyboard.setDown("lgui", true)
  local mods = utils.getModifiers()
  luaunit.assertTrue(mods.super)
  love.keyboard.setDown("lgui", false)
end

function TestInputUtils:testGetModifiers_MultipleModifiers()
  love.keyboard.setDown("lshift", true)
  love.keyboard.setDown("lctrl", true)
  local mods = utils.getModifiers()
  luaunit.assertTrue(mods.shift)
  luaunit.assertTrue(mods.ctrl)
  luaunit.assertFalse(mods.alt)
  luaunit.assertFalse(mods.super)

  -- Clean up
  love.keyboard.setDown("lshift", false)
  love.keyboard.setDown("lctrl", false)
end

-- Test suite for text size presets
TestTextSizePresets = {}

function TestTextSizePresets:testResolveTextSizePreset_ValidPresets()
  local value, unit = utils.resolveTextSizePreset("xs")
  luaunit.assertEquals(value, 1.25)
  luaunit.assertEquals(unit, "vh")

  value, unit = utils.resolveTextSizePreset("md")
  luaunit.assertEquals(value, 2.25)
  luaunit.assertEquals(unit, "vh")

  value, unit = utils.resolveTextSizePreset("xl")
  luaunit.assertEquals(value, 3.5)
  luaunit.assertEquals(unit, "vh")
end

function TestTextSizePresets:testResolveTextSizePreset_NumericValue()
  local value, unit = utils.resolveTextSizePreset(20)
  luaunit.assertNil(value)
  luaunit.assertNil(unit)
end

function TestTextSizePresets:testResolveTextSizePreset_InvalidPreset()
  local value, unit = utils.resolveTextSizePreset("invalid")
  luaunit.assertNil(value)
  luaunit.assertNil(unit)
end

function TestTextSizePresets:testResolveTextSizePreset_AllPresets()
  -- Test all available presets
  local presets = { "xxs", "2xs", "xs", "sm", "md", "lg", "xl", "xxl", "2xl", "3xl", "4xl" }
  for _, preset in ipairs(presets) do
    local value, unit = utils.resolveTextSizePreset(preset)
    luaunit.assertNotNil(value, "Preset " .. preset .. " should return a value")
    luaunit.assertEquals(unit, "vh", "Preset " .. preset .. " should return 'vh' unit")
  end
end

-- Test suite for enums
TestEnums = {}

function TestEnums:testEnums_Exist()
  luaunit.assertNotNil(utils.enums)
  luaunit.assertNotNil(utils.enums.TextAlign)
  luaunit.assertNotNil(utils.enums.Positioning)
  luaunit.assertNotNil(utils.enums.FlexDirection)
  luaunit.assertNotNil(utils.enums.JustifyContent)
  luaunit.assertNotNil(utils.enums.AlignItems)
  luaunit.assertNotNil(utils.enums.FlexWrap)
  luaunit.assertNotNil(utils.enums.TextSize)
end

function TestEnums:testEnums_TextAlign()
  luaunit.assertEquals(utils.enums.TextAlign.START, "start")
  luaunit.assertEquals(utils.enums.TextAlign.CENTER, "center")
  luaunit.assertEquals(utils.enums.TextAlign.END, "end")
  luaunit.assertEquals(utils.enums.TextAlign.JUSTIFY, "justify")
end

function TestEnums:testEnums_Positioning()
  luaunit.assertEquals(utils.enums.Positioning.ABSOLUTE, "absolute")
  luaunit.assertEquals(utils.enums.Positioning.RELATIVE, "relative")
  luaunit.assertEquals(utils.enums.Positioning.FLEX, "flex")
  luaunit.assertEquals(utils.enums.Positioning.GRID, "grid")
end

-- Run tests if this file is executed directly
if not _G.RUNNING_ALL_TESTS then
  os.exit(luaunit.LuaUnit.run())
end
