local log = require("neo-tree.log")
local utils = require("neo-tree.utils")
local M = {}

---@type integer
M.ns_id = vim.api.nvim_create_namespace("neo-tree.nvim")

M.BUFFER_NUMBER = "NeoTreeBufferNumber"
M.CURSOR_LINE = "NeoTreeCursorLine"
M.DIM_TEXT = "NeoTreeDimText"
M.DIRECTORY_ICON = "NeoTreeDirectoryIcon"
M.DIRECTORY_NAME = "NeoTreeDirectoryName"
M.DOTFILE = "NeoTreeDotfile"
M.FADE_TEXT_1 = "NeoTreeFadeText1"
M.FADE_TEXT_2 = "NeoTreeFadeText2"
M.FILE_ICON = "NeoTreeFileIcon"
M.FILE_NAME = "NeoTreeFileName"
M.FILE_NAME_OPENED = "NeoTreeFileNameOpened"
M.FILE_STATS = "NeoTreeFileStats"
M.FILE_STATS_HEADER = "NeoTreeFileStatsHeader"
M.FILTER_TERM = "NeoTreeFilterTerm"
M.FLOAT_BORDER = "NeoTreeFloatBorder"
M.FLOAT_NORMAL = "NeoTreeFloatNormal"
M.FLOAT_TITLE = "NeoTreeFloatTitle"
M.GIT_ADDED = "NeoTreeGitAdded"
M.GIT_CONFLICT = "NeoTreeGitConflict"
M.GIT_DELETED = "NeoTreeGitDeleted"
M.GIT_IGNORED = "NeoTreeGitIgnored"
M.GIT_MODIFIED = "NeoTreeGitModified"
M.GIT_RENAMED = "NeoTreeGitRenamed"
M.GIT_STAGED = "NeoTreeGitStaged"
M.GIT_UNTRACKED = "NeoTreeGitUntracked"
M.GIT_UNSTAGED = "NeoTreeGitUnstaged"
M.HIDDEN_BY_NAME = "NeoTreeHiddenByName"
M.INDENT_MARKER = "NeoTreeIndentMarker"
M.MESSAGE = "NeoTreeMessage"
M.MODIFIED = "NeoTreeModified"
M.NORMAL = "NeoTreeNormal"
M.NORMALNC = "NeoTreeNormalNC"
M.SIGNCOLUMN = "NeoTreeSignColumn"
M.STATUS_LINE = "NeoTreeStatusLine"
M.STATUS_LINE_NC = "NeoTreeStatusLineNC"
M.TAB_ACTIVE = "NeoTreeTabActive"
M.TAB_INACTIVE = "NeoTreeTabInactive"
M.TAB_SEPARATOR_ACTIVE = "NeoTreeTabSeparatorActive"
M.TAB_SEPARATOR_INACTIVE = "NeoTreeTabSeparatorInactive"
M.VERTSPLIT = "NeoTreeVertSplit"
M.WINSEPARATOR = "NeoTreeWinSeparator"
M.END_OF_BUFFER = "NeoTreeEndOfBuffer"
M.ROOT_NAME = "NeoTreeRootName"
M.SYMBOLIC_LINK_TARGET = "NeoTreeSymbolicLinkTarget"
M.TITLE_BAR = "NeoTreeTitleBar"
M.EXPANDER = "NeoTreeExpander"
M.WINDOWS_HIDDEN = "NeoTreeWindowsHidden"
M.PREVIEW = "NeoTreePreview"

---@param n integer
---@param chars integer?
local function dec_to_hex(n, chars)
  chars = chars or 6
  local hex = string.format("%0" .. chars .. "x", n)
  while #hex < chars do
    hex = "0" .. hex
  end
  return hex
end

---@param name string
local get_hl_by_name = function(name)
  if vim.api.nvim_get_hl then
    local hl = vim.api.nvim_get_hl(0, { name = name })
    ---@diagnostic disable-next-line: inject-field
    hl.foreground = hl.fg
    ---@diagnostic disable-next-line: inject-field
    hl.background = hl.bg
    return hl
  end
  ---TODO: remove in 4.0
  ---@diagnostic disable-next-line: deprecated
  return vim.api.nvim_get_hl_by_name(name, true)
end
---If the given highlight group is not defined, define it.
---@param hl_group_name string The name of the highlight group.
---@param link_to_if_exists string[] A list of highlight groups to link to, in order of priority. The first one that exists will be used.
---@param background string? The background color to use, in hex, if the highlight group is not defined and it is not linked to another group.
---@param foreground string? The foreground color to use, in hex, if the highlight group is not defined and it is not linked to another group.
---@param gui string? The gui to use, if the highlight group is not defined and it is not linked to another group.
---@return table hlgroups The highlight group values.
M.create_highlight_group = function(hl_group_name, link_to_if_exists, background, foreground, gui)
  local success, hl_group = pcall(get_hl_by_name, hl_group_name, true)
  if not success or not hl_group.foreground or not hl_group.background then
    for _, link_to in ipairs(link_to_if_exists) do
      success, hl_group = pcall(get_hl_by_name, link_to, true)
      if success then
        local new_group_has_settings = background or foreground or gui
        local link_to_has_settings = hl_group.foreground or hl_group.background
        if link_to_has_settings or not new_group_has_settings then
          vim.cmd("highlight default link " .. hl_group_name .. " " .. link_to)
          return hl_group
        end
      end
    end

    if type(background) == "number" then
      background = dec_to_hex(background)
    end
    if type(foreground) == "number" then
      foreground = dec_to_hex(foreground)
    end

    local cmd = "highlight default " .. hl_group_name
    if background then
      cmd = cmd .. " guibg=#" .. background
    end
    if foreground then
      cmd = cmd .. " guifg=#" .. foreground
    else
      cmd = cmd .. " guifg=NONE"
    end
    if gui then
      cmd = cmd .. " gui=" .. gui
    end
    vim.cmd(cmd)

    return {
      background = background and tonumber(background, 16) or nil,
      foreground = foreground and tonumber(foreground, 16) or nil,
    }
  end
  return hl_group
end

---@param hl_group_name string
---@param fade_percentage number
local calculate_faded_highlight_group = function(hl_group_name, fade_percentage)
  local normal = get_hl_by_name("Normal")
  if type(normal.foreground) ~= "number" then
    if vim.go.background == "dark" then
      normal.foreground = 0xffffff
    else
      normal.foreground = 0x000000
    end
  end
  if type(normal.background) ~= "number" then
    if vim.go.background == "dark" then
      normal.background = 0x000000
    else
      normal.background = 0xffffff
    end
  end
  local foreground = dec_to_hex(normal.foreground)
  local background = dec_to_hex(normal.background)

  local hl_group = get_hl_by_name(hl_group_name)
  if type(hl_group.foreground) == "number" then
    foreground = dec_to_hex(hl_group.foreground)
  end
  if type(hl_group.background) == "number" then
    background = dec_to_hex(hl_group.background)
  end

  local gui = {}
  if hl_group.bold then
    table.insert(gui, "bold")
  end
  if hl_group.italic then
    table.insert(gui, "italic")
  end
  if hl_group.underline then
    table.insert(gui, "underline")
  end
  if hl_group.undercurl then
    table.insert(gui, "undercurl")
  end

  local hl
  if #gui > 0 then
    hl = table.concat(gui, ",")
  end

  local f_red = tonumber(foreground:sub(1, 2), 16)
  local f_green = tonumber(foreground:sub(3, 4), 16)
  local f_blue = tonumber(foreground:sub(5, 6), 16)

  local b_red = tonumber(background:sub(1, 2), 16)
  local b_green = tonumber(background:sub(3, 4), 16)
  local b_blue = tonumber(background:sub(5, 6), 16)

  local red = (f_red * fade_percentage) + (b_red * (1 - fade_percentage))
  local green = (f_green * fade_percentage) + (b_green * (1 - fade_percentage))
  local blue = (f_blue * fade_percentage) + (b_blue * (1 - fade_percentage))

  local new_foreground =
    string.format("%s%s%s", dec_to_hex(red, 2), dec_to_hex(green, 2), dec_to_hex(blue, 2))

  return {
    background = hl_group.background,
    foreground = new_foreground,
    gui = hl,
  }
end

local faded_highlight_group_cache = {}
---@param hl_group_name string
---@param fade_percentage number
M.get_faded_highlight_group = function(hl_group_name, fade_percentage)
  if type(hl_group_name) ~= "string" then
    error("hl_group_name must be a string")
  end
  if type(fade_percentage) ~= "number" then
    error("hl_group_name must be a number")
  end
  if fade_percentage < 0 or fade_percentage > 1 then
    error("fade_percentage must be between 0 and 1")
  end

  local key = hl_group_name .. "_" .. tostring(math.floor(fade_percentage * 100))
  if faded_highlight_group_cache[key] then
    return faded_highlight_group_cache[key]
  end

  local faded = calculate_faded_highlight_group(hl_group_name, fade_percentage)

  M.create_highlight_group(key, {}, faded.background, faded.foreground, faded.gui)
  faded_highlight_group_cache[key] = key
  return key
end
local nvim_0_10 = vim.fn.has("nvim-0.10")
M.setup = function()
  local added_hl_name = nvim_0_10 and "Added" or "diffAdded"
  local changed_hl_name = nvim_0_10 and "Changed" or "diffChanged"
  local removed_hl_name = nvim_0_10 and "Removed" or "diffRemoved"
  -- Reset this here in case of color scheme change
  faded_highlight_group_cache = {}

  local normal_hl = M.create_highlight_group(M.NORMAL, { "Normal" })
  local normalnc_hl = M.create_highlight_group(M.NORMALNC, { "NormalNC", M.NORMAL })

  M.create_highlight_group(M.SIGNCOLUMN, { "SignColumn", M.NORMAL })

  M.create_highlight_group(M.STATUS_LINE, { "StatusLine" })
  M.create_highlight_group(M.STATUS_LINE_NC, { "StatusLineNC" })

  M.create_highlight_group(M.VERTSPLIT, { "VertSplit" })
  M.create_highlight_group(M.WINSEPARATOR, { "WinSeparator" })

  M.create_highlight_group(M.END_OF_BUFFER, { "EndOfBuffer" })

  local float_border_hl =
    M.create_highlight_group(M.FLOAT_BORDER, { "FloatBorder" }, normalnc_hl.background, "444444")

  M.create_highlight_group(M.FLOAT_NORMAL, { "NormalFloat", M.NORMAL })

  M.create_highlight_group(M.FLOAT_TITLE, {}, float_border_hl.background, normal_hl.foreground)

  local title_fg = normal_hl.background
  if title_fg == float_border_hl.foreground then
    title_fg = normal_hl.foreground
  end
  M.create_highlight_group(M.TITLE_BAR, {}, float_border_hl.foreground, title_fg)

  local dim_text = calculate_faded_highlight_group("NeoTreeNormal", 0.3)

  M.create_highlight_group(M.BUFFER_NUMBER, { "SpecialChar" })
  --M.create_highlight_group(M.DIM_TEXT, {}, nil, "505050")
  M.create_highlight_group(M.MESSAGE, {}, nil, dim_text.foreground, "italic")
  M.create_highlight_group(M.FADE_TEXT_1, {}, nil, "626262")
  M.create_highlight_group(M.FADE_TEXT_2, {}, nil, "444444")
  M.create_highlight_group(M.DOTFILE, {}, nil, "626262")
  M.create_highlight_group(M.HIDDEN_BY_NAME, { M.DOTFILE }, nil, nil)
  M.create_highlight_group(M.CURSOR_LINE, { "CursorLine" }, nil, nil, "bold")
  M.create_highlight_group(M.DIM_TEXT, {}, nil, dim_text.foreground)
  M.create_highlight_group(M.DIRECTORY_NAME, { "Directory" }, "NONE", "NONE")
  M.create_highlight_group(M.DIRECTORY_ICON, { "Directory" }, nil, "73cef4")
  M.create_highlight_group(M.FILE_ICON, { M.DIRECTORY_ICON })
  M.create_highlight_group(M.FILE_NAME, {}, "NONE", "NONE")
  M.create_highlight_group(M.FILE_NAME_OPENED, {}, nil, nil, "bold")
  M.create_highlight_group(M.SYMBOLIC_LINK_TARGET, { M.FILE_NAME })
  M.create_highlight_group(M.FILTER_TERM, { "SpecialChar", "Normal" })
  M.create_highlight_group(M.ROOT_NAME, {}, nil, nil, "bold,italic")
  M.create_highlight_group(M.INDENT_MARKER, { M.DIM_TEXT })
  M.create_highlight_group(M.EXPANDER, { M.DIM_TEXT })
  M.create_highlight_group(M.MODIFIED, {}, nil, "d7d787")
  M.create_highlight_group(M.WINDOWS_HIDDEN, { M.DOTFILE }, nil, nil)
  M.create_highlight_group(M.PREVIEW, { "Search" }, nil, nil)

  M.create_highlight_group(
    M.GIT_ADDED,
    { "GitGutterAdd", "GitSignsAdd", added_hl_name },
    nil,
    "5faf5f"
  )
  M.create_highlight_group(
    M.GIT_DELETED,
    { "GitGutterDelete", "GitSignsDelete", removed_hl_name },
    nil,
    "ff5900"
  )
  M.create_highlight_group(
    M.GIT_MODIFIED,
    { "GitGutterChange", "GitSignsChange", changed_hl_name },
    nil,
    "d7af5f"
  )
  local conflict = M.create_highlight_group(M.GIT_CONFLICT, {}, nil, "ff8700", "italic,bold")
  M.create_highlight_group(M.GIT_IGNORED, { M.DOTFILE }, nil, nil)
  M.create_highlight_group(M.GIT_RENAMED, { M.GIT_MODIFIED }, nil, nil)
  M.create_highlight_group(M.GIT_STAGED, { M.GIT_ADDED }, nil, nil)
  M.create_highlight_group(M.GIT_UNSTAGED, { M.GIT_CONFLICT }, nil, nil)
  M.create_highlight_group(M.GIT_UNTRACKED, {}, nil, conflict.foreground, "italic")

  M.create_highlight_group(M.TAB_ACTIVE, {}, nil, nil, "bold")
  M.create_highlight_group(M.TAB_INACTIVE, {}, "141414", "777777")
  M.create_highlight_group(M.TAB_SEPARATOR_ACTIVE, {}, nil, "0a0a0a")
  M.create_highlight_group(M.TAB_SEPARATOR_INACTIVE, {}, "141414", "101010")

  local faded_normal = calculate_faded_highlight_group("NeoTreeNormal", 0.4)
  M.create_highlight_group(M.FILE_STATS, {}, nil, faded_normal.foreground)

  local faded_root = calculate_faded_highlight_group("NeoTreeRootName", 0.5)
  M.create_highlight_group(M.FILE_STATS_HEADER, {}, nil, faded_root.foreground, faded_root.gui)
end

return M
