--
-- Copyright (c) 2021-2025 Zeping Lee
-- Released under the MIT license.
-- Repository: https://github.com/zepinglee/citeproc-lua
--

local irnode = {}

local util

local using_luatex, kpse = pcall(require, "kpse")
if using_luatex then
  util = require("citeproc-util")
else
  util = require("citeproc.util")
end


---@enum GroupVar
local GroupVar = {
  Plain = 0,
  Important = 1,
  Missing = 2,
  UnresolvedPlain = 3,
}


---@class IrNode
---@field _element Element?
---@field _element_name string?
---@field _type string
---@field _base_class string
---@field text string?
---@field formatting any
---@field affixes any
---@field display any
---@field quotes LocalizedQuotes
---@field inlines InlineElement[]?
---@field children IrNode[]?
---@field delimiter string?
---@field should_inherit_delim boolean?
---@field group_var GroupVar
---@field sort_key string | boolean?
---@field person_name_irs IrNode[]
---@field name_count integer?
---@field is_year boolean?
local IrNode = {
  _element = nil,
  _element_name = nil,
  _type = "IrNode",
  _base_class = "IrNode",
  text = nil,
  formatting = nil,
  affixes = nil,
  children = nil,
  delimiter = nil,
}

---@param children IrNode[]
---@param element Element
---@return IrNode
function IrNode:new(children, element)
  local o = {
    _element = element,
    _element_name = element.element_name,
    _type = self._type,
    children = children,
    group_var = GroupVar.Plain,
  }

  o.group_var = GroupVar.Missing
  for _, child_ir in ipairs(children) do
    if child_ir.group_var == GroupVar.Important then
      o.group_var = GroupVar.Important
      break
    elseif child_ir.group_var == GroupVar.Plain then
      o.group_var = GroupVar.Plain
    end
  end

  o.person_name_irs = {}
  if children then
    for _, child in ipairs(children) do
      if child.person_name_irs then
        util.extend(o.person_name_irs, child.person_name_irs)
      end
    end
  end

  setmetatable(o, self)
  self.__index = self
  return o
end

function IrNode:derive(type)
  local o = {
    _type = type,
  }
  setmetatable(o, self)
  self.__index = self
  return o
end

---@param level integer
---@return string
function IrNode:_debug(level)
  level = level or 0
  local ir_info_str = ""
  local ir_info = {}
  if self.group_var == GroupVar.Plain then
    table.insert(ir_info, "[Plain]")
  elseif self.group_var == GroupVar.Important then
    table.insert(ir_info, "[Important]")
  elseif self.group_var == GroupVar.Missing then
    table.insert(ir_info, "[Missing]")
  elseif self.group_var == GroupVar.UnresolvedPlain then
    table.insert(ir_info, "[UnresolvedPlain]")
  end
  if self.delimiter then
    table.insert(ir_info, string.format('delimiter: "%s"', self.delimiter))
  end
  if self.should_inherit_delim then
    table.insert(ir_info, "should_inherit_delim: true")
  end
  if self.person_name_irs then
    table.insert(ir_info, "person_name_irs: " .. tostring(#self.person_name_irs))
  end
  if #ir_info > 0 then
    ir_info_str = string.format("{%s}", table.concat(ir_info, " "))
  end
  local element_info = self._element.element_name
  for _, attr in ipairs({"name", "variable"}) do
    if self._element[attr] then
      element_info = element_info .. string.format(' %s="%s"', attr, self._element[attr])
    end
  end
  local text = string.format("\n%s [%s] %s <%s> %s", string.rep("    ", level), self.group_var, self._type,
    element_info, ir_info_str)
  if self.children and #self.children > 0 then
    for _, child_ir in ipairs(self.children) do
      text = text .. child_ir:_debug(level + 1)
    end

  elseif self.inlines then
    for _, inline in ipairs(self.inlines) do
      text = text .. " " .. inline:_debug()
    end
  end
  return text
end

function IrNode:flatten(format)
  return format:flatten_ir(self)
end

function IrNode:capitalize_first_term()
  if self._type == "Rendered" and self._element then
    local element = self._element
    ---@cast element Text
    if (element.term == "ibid" or element.term == "and") then
      self.inlines[1]:capitalize_first_term()
    end
  elseif self._type == "SeqIr" and self.children[1] then
    self.children[1]:capitalize_first_term()
  end
end

function IrNode:collect_year_suffix_irs()
  local year_suffix_irs = {}
  if self.children then
    for i, child_ir in ipairs(self.children) do
      if child_ir._type == "YearSuffix" then
        table.insert(year_suffix_irs, child_ir)
      elseif child_ir.children then
        util.extend(year_suffix_irs,
          child_ir:collect_year_suffix_irs())
      end
    end
  end
  return year_suffix_irs
end

function IrNode:find_first_year_ir()
  -- This also find the citation-label IR
  if self.is_year then
    return self
  end
  if self.children then
    for _, child_ir in ipairs(self.children) do
      local year_ir = child_ir:find_first_year_ir()
      if year_ir then
        return year_ir
      end
    end
  end
  return nil
end


---@class Rendered: IrNode
local Rendered = IrNode:derive("Rendered")

function Rendered:new(inlines, element)
  local o = {
    _element = element,
    _element_name = element.element_name,
    _type = self._type,
    element = element,  -- required for capitalizing first term
    inlines = inlines,
    group_var = GroupVar.Plain,
  }

  setmetatable(o, self)
  self.__index = self
  return o
end


---@class YearSuffix: IrNode
local YearSuffix = IrNode:derive("YearSuffix")

function YearSuffix:new(inlines, element)
  local o = {
    _element = element,
    _element_name = element.element_name,
    _type = self._type,
    element = element,
    inlines = inlines,
    group_var = GroupVar.Plain,
  }

  setmetatable(o, self)
  self.__index = self
  return o
end


---@class NameIr: IrNode
local NameIr = IrNode:derive("NameIr")


---@class PersonNameIr: IrNode
local PersonNameIr = IrNode:derive("PersonNameIr")

function PersonNameIr:new(inlines, element)
  local o = {
    _element = element,
    _element_name = element.element_name,
    _type = self._type,
    inlines = inlines,
    group_var = GroupVar.Plain,
  }
  setmetatable(o, self)
  self.__index = self
  return o
end


---@class SeqIr: IrNode
---@field children IrNode[]
---@field disambiguate_branch_ir IrNode?
local SeqIr = IrNode:derive("SeqIr")

-- function SeqIr:new(children)
--   o = IrNode.new(self, children)
--   local o = {
--     children = children,
--     group_var = GroupVar.Plain,
--   }
--   setmetatable(o, self)
--   self.__index = self
--   return o
-- end


irnode.IrNode = IrNode
irnode.Rendered = Rendered
irnode.YearSuffix = YearSuffix
irnode.NameIr = NameIr
irnode.PersonNameIr = PersonNameIr
irnode.SeqIr = SeqIr

irnode.GroupVar = GroupVar

return irnode
