local ErrorHandler = nil

--- Initialize ErrorHandler dependency
---@param errorHandler table The ErrorHandler module
local function initializeErrorHandler(errorHandler)
  ErrorHandler = errorHandler
end

--- Standardized error message formatter (fallback for when ErrorHandler not available)
---@param module string -- Module name (e.g., "Color", "Theme", "Units")
---@param message string
---@return string
local function formatError(module, message)
  return string.format("[FlexLove.%s] %s", module, message)
end

-- Named colors (CSS3 color names)
local NAMED_COLORS = {
  -- Basic colors
  black = {0, 0, 0, 1},
  white = {1, 1, 1, 1},
  red = {1, 0, 0, 1},
  green = {0, 0.502, 0, 1},
  blue = {0, 0, 1, 1},
  yellow = {1, 1, 0, 1},
  cyan = {0, 1, 1, 1},
  magenta = {1, 0, 1, 1},
  
  -- Extended colors
  gray = {0.502, 0.502, 0.502, 1},
  grey = {0.502, 0.502, 0.502, 1},
  silver = {0.753, 0.753, 0.753, 1},
  maroon = {0.502, 0, 0, 1},
  olive = {0.502, 0.502, 0, 1},
  lime = {0, 1, 0, 1},
  aqua = {0, 1, 1, 1},
  teal = {0, 0.502, 0.502, 1},
  navy = {0, 0, 0.502, 1},
  fuchsia = {1, 0, 1, 1},
  purple = {0.502, 0, 0.502, 1},
  orange = {1, 0.647, 0, 1},
  pink = {1, 0.753, 0.796, 1},
  brown = {0.647, 0.165, 0.165, 1},
  transparent = {0, 0, 0, 0},
}

--- Utility class for color handling
---@class Color
---@field r number -- Red component (0-1)
---@field g number -- Green component (0-1)
---@field b number -- Blue component (0-1)
---@field a number -- Alpha component (0-1)
local Color = {}
Color.__index = Color

--- Create a new color instance
---@param r number? Red component (0-1), defaults to 0
---@param g number? Green component (0-1), defaults to 0
---@param b number? Blue component (0-1), defaults to 0
---@param a number? Alpha component (0-1), defaults to 1
---@return Color color The new color instance
function Color.new(r, g, b, a)
  local self = setmetatable({}, Color)
  
  -- Sanitize and clamp color components
  local _, sanitizedR = Color.validateColorChannel(r or 0, 1)
  local _, sanitizedG = Color.validateColorChannel(g or 0, 1)
  local _, sanitizedB = Color.validateColorChannel(b or 0, 1)
  local _, sanitizedA = Color.validateColorChannel(a or 1, 1)
  
  self.r = sanitizedR or 0
  self.g = sanitizedG or 0
  self.b = sanitizedB or 0
  self.a = sanitizedA or 1
  return self
end

---Convert color to RGBA components
---@return number r Red component (0-1)
---@return number g Green component (0-1)
---@return number b Blue component (0-1)
---@return number a Alpha component (0-1)
function Color:toRGBA()
  return self.r, self.g, self.b, self.a
end

--- Convert hex string to color
--- Supports both 6-digit (#RRGGBB) and 8-digit (#RRGGBBAA) hex formats
---@param hexWithTag string Hex color string (e.g. "#RRGGBB" or "#RRGGBBAA")
---@return Color color The parsed color (returns white on error with warning)
function Color.fromHex(hexWithTag)
  -- Validate input type
  if type(hexWithTag) ~= "string" then
    if ErrorHandler then
      ErrorHandler.warn("Color", "VAL_004", "Invalid color format", {
        input = tostring(hexWithTag),
        issue = "not a string",
        fallback = "white (#FFFFFF)"
      })
    end
    return Color.new(1, 1, 1, 1)
  end
  
  local hex = hexWithTag:gsub("#", "")
  if #hex == 6 then
    local r = tonumber("0x" .. hex:sub(1, 2))
    local g = tonumber("0x" .. hex:sub(3, 4))
    local b = tonumber("0x" .. hex:sub(5, 6))
    if not r or not g or not b then
      if ErrorHandler then
        ErrorHandler.warn("Color", "VAL_004", "Invalid color format", {
          input = hexWithTag,
          issue = "invalid hex digits",
          fallback = "white (#FFFFFF)"
        })
      end
      return Color.new(1, 1, 1, 1) -- Return white as fallback
    end
    return Color.new(r / 255, g / 255, b / 255, 1)
  elseif #hex == 8 then
    local r = tonumber("0x" .. hex:sub(1, 2))
    local g = tonumber("0x" .. hex:sub(3, 4))
    local b = tonumber("0x" .. hex:sub(5, 6))
    local a = tonumber("0x" .. hex:sub(7, 8))
    if not r or not g or not b or not a then
      if ErrorHandler then
        ErrorHandler.warn("Color", "VAL_004", "Invalid color format", {
          input = hexWithTag,
          issue = "invalid hex digits",
          fallback = "white (#FFFFFFFF)"
        })
      end
      return Color.new(1, 1, 1, 1) -- Return white as fallback
    end
    return Color.new(r / 255, g / 255, b / 255, a / 255)
  else
    if ErrorHandler then
      ErrorHandler.warn("Color", "VAL_004", "Invalid color format", {
        input = hexWithTag,
        expected = "#RRGGBB or #RRGGBBAA",
        hexLength = #hex,
        fallback = "white (#FFFFFF)"
      })
    end
    return Color.new(1, 1, 1, 1) -- Return white as fallback
  end
end

--- Validate a single color channel value
---@param value any Value to validate
---@param max number? Maximum value (255 for 0-255 range, 1 for 0-1 range), defaults to 1
---@return boolean valid True if valid
---@return number? clamped Clamped value in 0-1 range, nil if invalid
function Color.validateColorChannel(value, max)
  max = max or 1
  
  if type(value) ~= "number" then
    return false, nil
  end
  
  -- Check for NaN
  if value ~= value then
    return false, nil
  end
  
  -- Check for Infinity
  if value == math.huge or value == -math.huge then
    return false, nil
  end
  
  -- Normalize to 0-1 range
  local normalized = value
  if max == 255 then
    normalized = value / 255
  end
  
  -- Clamp to valid range
  normalized = math.max(0, math.min(1, normalized))
  
  return true, normalized
end

--- Validate hex color format
---@param hex string Hex color string (with or without #)
---@return boolean valid True if valid format
---@return string? error Error message if invalid, nil if valid
function Color.validateHexColor(hex)
  if type(hex) ~= "string" then
    return false, "Hex color must be a string"
  end
  
  -- Remove # prefix
  local cleanHex = hex:gsub("^#", "")
  
  -- Check length (3, 6, or 8 characters)
  if #cleanHex ~= 3 and #cleanHex ~= 6 and #cleanHex ~= 8 then
    return false, string.format("Invalid hex length: %d. Expected 3, 6, or 8 characters", #cleanHex)
  end
  
  -- Check for valid hex characters
  if not cleanHex:match("^[0-9A-Fa-f]+$") then
    return false, "Invalid hex characters. Use only 0-9, A-F"
  end
  
  return true, nil
end

--- Validate RGB/RGBA color values
---@param r number Red component
---@param g number Green component
---@param b number Blue component
---@param a number? Alpha component (optional, defaults to max)
---@param max number? Maximum value (255 or 1), defaults to 1
---@return boolean valid True if valid
---@return string? error Error message if invalid, nil if valid
function Color.validateRGBColor(r, g, b, a, max)
  max = max or 1
  a = a or max
  
  local rValid = Color.validateColorChannel(r, max)
  local gValid = Color.validateColorChannel(g, max)
  local bValid = Color.validateColorChannel(b, max)
  local aValid = Color.validateColorChannel(a, max)
  
  if not rValid then
    return false, string.format("Invalid red channel: %s", tostring(r))
  end
  if not gValid then
    return false, string.format("Invalid green channel: %s", tostring(g))
  end
  if not bValid then
    return false, string.format("Invalid blue channel: %s", tostring(b))
  end
  if not aValid then
    return false, string.format("Invalid alpha channel: %s", tostring(a))
  end
  
  return true, nil
end

--- Validate named color
---@param name string Color name (e.g. "red", "blue", "transparent")
---@return boolean valid True if valid
---@return string? error Error message if invalid, nil if valid
function Color.validateNamedColor(name)
  if type(name) ~= "string" then
    return false, "Color name must be a string"
  end
  
  local lowerName = name:lower()
  if not NAMED_COLORS[lowerName] then
    return false, string.format("Unknown color name: '%s'", name)
  end
  
  return true, nil
end

--- Check if a value is a valid color format
---@param value any Value to check
---@return string? format Format type ("hex", "named", "table"), nil if invalid
function Color.isValidColorFormat(value)
  local valueType = type(value)
  
  -- Check for hex string
  if valueType == "string" then
    if value:match("^#?[0-9A-Fa-f]+$") then
      local valid = Color.validateHexColor(value)
      if valid then
        return "hex"
      end
    end
    
    -- Check for named color
    if NAMED_COLORS[value:lower()] then
      return "named"
    end
    
    return nil
  end
  
  -- Check for table format
  if valueType == "table" then
    -- Check for Color instance
    if getmetatable(value) == Color then
      return "table"
    end
    
    -- Check for array format {r, g, b, a}
    if value[1] and value[2] and value[3] then
      local valid = Color.validateRGBColor(value[1], value[2], value[3], value[4])
      if valid then
        return "table"
      end
    end
    
    -- Check for named format {r=, g=, b=, a=}
    if value.r and value.g and value.b then
      local valid = Color.validateRGBColor(value.r, value.g, value.b, value.a)
      if valid then
        return "table"
      end
    end
    
    return nil
  end
  
  return nil
end

--- Validate a color value
---@param value any Color value to validate
---@param options table? Validation options {allowNamed: boolean, requireAlpha: boolean}
---@return boolean valid True if valid
---@return string? error Error message if invalid, nil if valid
function Color.validateColor(value, options)
  options = options or {}
  local allowNamed = options.allowNamed ~= false
  local requireAlpha = options.requireAlpha or false
  
  if value == nil then
    return false, "Color value is nil"
  end
  
  local format = Color.isValidColorFormat(value)
  
  if not format then
    return false, string.format("Invalid color format: %s", tostring(value))
  end
  
  if format == "named" and not allowNamed then
    return false, "Named colors not allowed"
  end
  
  -- Additional validation for alpha requirement
  if requireAlpha and format == "hex" then
    local cleanHex = value:gsub("^#", "")
    if #cleanHex ~= 8 then
      return false, "Alpha channel required (use 8-digit hex)"
    end
  end
  
  return true, nil
end

--- Sanitize a color value (always returns a valid Color)
---@param value any Color value to sanitize (hex, named, table, or Color instance)
---@param default Color? Default color if invalid (defaults to black)
---@return Color color Sanitized color instance (guaranteed non-nil)
function Color.sanitizeColor(value, default)
  default = default or Color.new(0, 0, 0, 1)
  
  local format = Color.isValidColorFormat(value)
  
  if not format then
    return default
  end
  
  -- Handle hex format
  if format == "hex" then
    local cleanHex = value:gsub("^#", "")
    
    -- Expand 3-digit hex to 6-digit
    if #cleanHex == 3 then
      cleanHex = cleanHex:gsub("(.)", "%1%1")
    end
    
    -- Try to parse
    local success, result = pcall(Color.fromHex, "#" .. cleanHex)
    if success then
      return result
    else
      return default
    end
  end
  
  -- Handle named format
  if format == "named" then
    local lowerName = value:lower()
    local rgba = NAMED_COLORS[lowerName]
    if rgba then
      return Color.new(rgba[1], rgba[2], rgba[3], rgba[4])
    end
    return default
  end
  
  -- Handle table format
  if format == "table" then
    -- Color instance
    if getmetatable(value) == Color then
      return value
    end
    
    -- Array format
    if value[1] then
      local _, r = Color.validateColorChannel(value[1], 1)
      local _, g = Color.validateColorChannel(value[2], 1)
      local _, b = Color.validateColorChannel(value[3], 1)
      local _, a = Color.validateColorChannel(value[4] or 1, 1)
      
      if r and g and b and a then
        return Color.new(r, g, b, a)
      end
    end
    
    -- Named format
    if value.r then
      local _, r = Color.validateColorChannel(value.r, 1)
      local _, g = Color.validateColorChannel(value.g, 1)
      local _, b = Color.validateColorChannel(value.b, 1)
      local _, a = Color.validateColorChannel(value.a or 1, 1)
      
      if r and g and b and a then
        return Color.new(r, g, b, a)
      end
    end
  end
  
  return default
end

--- Parse a color from various formats (always returns a valid Color)
---@param value any Color value (hex string, named color, table, or Color instance)
---@return Color color Parsed color instance (defaults to black on error)
function Color.parse(value)
  return Color.sanitizeColor(value, Color.new(0, 0, 0, 1))
end

-- Export ErrorHandler initializer
Color.initializeErrorHandler = initializeErrorHandler

return Color
