-- DONT EDIT THIS --
local M = {}

-- Sandbox environment for safe Lua execution
local function create_sandbox(theme)
  return setmetatable({
    theme = theme,
    math = math,
    os = { date = os.date, time = os.time, difftime = os.difftime },
    string = string,
    table = table,
    -- Limited math functions
    clamp = function(v, min, max) return math.min(math.max(v, min), max) end,
    lerp = function(a, b, t) return a + (b - a) * t end,
    -- Color utilities
    rgb = function(r, g, b) return { r = r/255, g = g/255, b = b/255 } end,
    rgba = function(r, g, b, a) return { r = r/255, g = g/255, b = b/255, a = a } end
  }, {
    __index = function(_, k)
      error(string.format("Attempt to access disallowed global '%s'", k))
    end,
    __newindex = function(_, k, _)
      error(string.format("Attempt to create global '%s'", k))
    end
  })
end

-- Parse and evaluate Lua style expressions safely
local function eval_style(expr, theme)
  if type(expr) == "string" and expr:sub(1, 6) == "return" then
    local chunk = "return "..expr:sub(7)
    local fn, err = load(chunk, "style", "t", create_sandbox(theme))
    if not fn then
      print("EZTerm Lua Style Error:", err)
      return nil
    end
    local success, result = pcall(fn)
    return success and result or nil
  end
  return expr
end

-- Theme management
function M.load_theme(config)
  local theme = {
    name = config.active,
    colors = {},
    raw = config.theme.available[config.active] or {}
  }

  -- Evaluate all color values
  for k, v in pairs(theme.raw.colors or {}) do
    if type(v) == "table" then
      theme.colors[k] = {}
      for i, color in ipairs(v) do
        theme.colors[k][i] = eval_style(color, theme)
      end
    else
      theme.colors[k] = eval_style(v, theme)
    end
  end

  -- Load Lua theme functions
  theme.functions = {}
  for name, lua_code in pairs(config.theme.lua_theme_functions or {}) do
    local fn, err = load(lua_code, name, "t", create_sandbox(theme))
    if fn then
      theme.functions[name] = function(...)
        local success, result = pcall(fn, ...)
        return success and result or nil
      end
    else
      print("EZTerm Lua Function Error:", err)
    end
  end

  return theme
end

-- UI Style Processor
function M.process_ui_config(config, theme)
  local styles = {
    layout = {},
    elements = {},
    text = {},
    animations = {}
  }

  -- Process layout styles
  for k, expr in pairs(config.ui.layout or {}) do
    styles.layout[k] = eval_style(expr, theme)
  end

  -- Process UI elements
  for element, props in pairs(config.ui.elements or {}) do
    styles.elements[element] = {}
    for prop, expr in pairs(props) do
      styles.elements[element][prop] = eval_style(expr, theme)
    end
  end

  -- Process text styles
  for k, expr in pairs(config.text or {}) do
    if type(expr) == "table" then
      styles.text[k] = {}
      for prop, val in pairs(expr) do
        styles.text[k][prop] = eval_style(val, theme)
      end
    else
      styles.text[k] = eval_style(expr, theme)
    end
  end

  -- Process animations
  styles.animations.enabled = eval_style(config.animations.enabled, theme)
  styles.animations.fps = eval_style(config.animations.fps, theme)
  styles.animations.effects = {}

  for effect, props in pairs(config.animations.effects or {}) do
    styles.animations.effects[effect] = {}
    for prop, expr in pairs(props) do
      styles.animations.effects[effect][prop] = eval_style(expr, theme)
    end
  end

  return styles
end

-- Animation System
function M.create_animation_system(styles)
  local system = {
    active_animations = {},
    last_frame_time = 0,
    fps = styles.animations.fps or 60,
    frame_delay = 1 / (styles.animations.fps or 60)
  }

  function system:update()
    local current_time = os.clock()
    local delta = current_time - self.last_frame_time
    
    if delta >= self.frame_delay then
      self.last_frame_time = current_time
      
      -- Update all active animations
      for id, anim in pairs(self.active_animations) do
        anim.progress = math.min(1, anim.progress + (delta / anim.duration))
        
        if anim.progress == 1 then
          if anim.on_complete then anim.on_complete() end
          self.active_animations[id] = nil
        end
      end
    end
  end

  function system:animate(target, properties, duration, easing, on_complete)
    local id = #self.active_animations + 1
    local start_values = {}
    
    for prop, _ in pairs(properties) do
      start_values[prop] = target[prop]
    end
    
    self.active_animations[id] = {
      target = target,
      start = start_values,
      properties = properties,
      duration = duration,
      easing = easing or function(t) return t end,
      progress = 0,
      on_complete = on_complete
    }
    
    return id
  end

  return system
end

-- Hooks System
function M.setup_hooks(config, theme)
  local hooks = {
    pre_render = function() end,
    post_render = function() end
  }

  -- Load pre-render hook
  if config.hooks and config.hooks.pre_render then
    local fn, err = load(config.hooks.pre_render, "pre_render", "t", create_sandbox(theme))
    if fn then
      hooks.pre_render = function()
        local success, err = pcall(fn)
        if not success then
          print("EZTerm pre_render hook error:", err)
        end
      end
    else
      print("EZTerm failed to load pre_render hook:", err)
    end
  end

  -- Load post-render hook
  if config.hooks and config.hooks.post_render then
    local fn, err = load(config.hooks.post_render, "post_render", "t", create_sandbox(theme))
    if fn then
      hooks.post_render = function()
        local success, err = pcall(fn)
        if not success then
          print("EZTerm post_render hook error:", err)
        end
      end
    else
      print("EZTerm failed to load post_render hook:", err)
    end
  end

  return hooks
end

-- Dynamic Value Cache
function M.create_dynamic_cache(theme)
  local cache = {
    values = {},
    theme = theme
  }

  function cache:get(key)
    if self.values[key] and self.values[key].expires > os.time() then
      return self.values[key].value
    end
    
    if self.theme.functions[key] then
      local value = self.theme.functions[key]()
      self.values[key] = {
        value = value,
        expires = os.time() + 1 -- Cache for 1 second
      }
      return value
    end
    
    return nil
  end

  return cache
end

-- Main Stylization Controller
function M.create_style_controller(config_path)
  local controller = {
    config = {},
    theme = {},
    styles = {},
    needs_redraw = true
  }

  -- Load configuration
  local config_file = io.open(config_path, "r")
  if config_file then
    local content = config_file:read("*a")
    config_file:close()
    controller.config = json.decode(content)
  else
    error("Failed to load EZTerm style config: "..config_path)
  end

  -- Initialize systems
  controller.theme = M.load_theme(controller.config)
  controller.styles = M.process_ui_config(controller.config, controller.theme)
  controller.animation = M.create_animation_system(controller.styles)
  controller.hooks = M.setup_hooks(controller.config, controller.theme)
  controller.cache = M.create_dynamic_cache(controller.theme)

  -- Public interface
  function controller:update()
    self.animation:update()
    
    -- Check dynamic values
    for name, _ in pairs(self.theme.functions) do
      local current = self.cache:get(name)
      if current ~= self.cache.values[name] then
        self.needs_redraw = true
      end
    end
  end

  function controller:get_color(name, index)
    if index and self.theme.colors[name] and self.theme.colors[name][index] then
      return self.theme.colors[name][index]
    end
    return self.theme.colors[name]
  end

  function controller:get_style(path)
    local parts = {}
    for part in path:gmatch("[^%.]+") do
      table.insert(parts, part)
    end

    local value = self.styles
    for _, part in ipairs(parts) do
      value = value[part]
      if not value then return nil end
    end

    return value
  end

  return controller
end

return M