local conditions = require('heirline.conditions')
local utils = require('heirline.utils')

local Spacer = { provider = ' ' }
local function rpad(child)
  return {
    condition = child.condition,
    child,
    Spacer,
  }
end
local function lpad(child)
  return {
    condition = child.condition,
    Spacer,
    child,
  }
end

local stl_static = {
  mode_color_map = {
    n = 'function',
    i = 'green',
    v = 'statement',
    V = 'statement',
    ['\22'] = 'statement',
    c = 'yellow',
    s = 'statement',
    S = 'statement',
    ['\19'] = 'statement',
    R = 'red',
    r = 'red',
    ['!'] = 'constant',
    t = 'constant',
  },
  mode_color = function(self)
    ---@diagnostic disable-next-line: redundant-parameter
    local mode = vim.fn.mode(1):sub(1, 1) -- get only the first mode character
    return self.mode_color_map[mode]
  end,
}

local ViMode = {
  init = function(self)
    ---@diagnostic disable-next-line: redundant-parameter
    self.mode = vim.fn.mode(1) -- :h mode()

    -- execute this only once, this is required if you want the ViMode
    -- component to be updated on operator pending mode
    if not self.once then
      vim.api.nvim_create_autocmd('ModeChanged', {
        pattern = '*:*o',
        command = 'redrawstatus',
      })
      self.once = true
    end
  end,
  provider = function(_)
    return ' '
  end,
  hl = function(self)
    return { fg = 'black', bg = self:mode_color(), bold = true }
  end,
  update = {
    'ModeChanged',
  },
}

local FileIcon = {
  init = function(self)
    self.icon, self.icon_color =
      require('nvim-web-devicons').get_icon_color_by_filetype(vim.bo.filetype, { default = true })
  end,
  provider = function(self)
    return self.icon and (self.icon .. ' ')
  end,
  hl = function(self)
    return { fg = self.icon_color }
  end,
}

local FileType = {
  condition = function()
    return vim.bo.filetype ~= ''
  end,
  FileIcon,
  {
    provider = function()
      return vim.bo.filetype
    end,
  },
}

local FileName = {
  provider = function(_)
    local filename = vim.fn.fnamemodify(vim.api.nvim_buf_get_name(0), ':.')
    if filename == '' then
      return '[No Name]'
    end
    -- now, if the filename would occupy more than 90% of the available
    -- space, we trim the file path to its initials
    if not conditions.width_percent_below(#filename, 0.90) then
      filename = vim.fn.pathshorten(filename)
    end
    return filename
  end,
}

local FileFlags = {
  {
    condition = function()
      return vim.bo.modified
    end,
    provider = ' [+]',
  },
  {
    condition = function()
      return not vim.bo.modifiable or vim.bo.readonly
    end,
    provider = ' ',
  },
}

local FullFileName = {
  hl = function()
    local fg
    if vim.bo.modified then
      fg = 'yellow'
    else
      fg = conditions.is_active() and 'tablinesel_fg' or 'tabline_fg'
    end
    return {
      fg = fg,
      bg = conditions.is_active() and 'tablinesel_bg' or 'winbar_bg',
    }
  end,
  FileName,
  FileFlags,
  { provider = '%=' },
}

local Diagnostics = {
  condition = function()
    return #vim.diagnostic.get(0, { severity = { min = vim.diagnostic.severity.WARN } }) > 0
  end,
  static = {
    error_icon = vim.fn.sign_getdefined('DiagnosticSignError')[1].text,
    warn_icon = vim.fn.sign_getdefined('DiagnosticSignWarn')[1].text,
  },
  init = function(self)
    self.errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR })
    self.warnings = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN })
  end,
  update = { 'DiagnosticChanged', 'BufEnter' },

  {
    provider = function(self)
      return self.errors > 0 and (self.error_icon .. self.errors .. ' ')
    end,
    hl = { fg = 'diag_error' },
  },
  {
    provider = function(self)
      return self.warnings > 0 and (self.warn_icon .. self.warnings .. ' ')
    end,
    hl = { fg = 'diag_warn' },
  },
}

local function setup_colors()
  return {
    fg = utils.get_highlight('StatusLine').fg or 'none',
    bg = utils.get_highlight('StatusLine').bg or 'none',
    winbar_fg = utils.get_highlight('WinBar').fg or 'none',
    winbar_bg = utils.get_highlight('WinBar').bg or 'none',
    tablinesel_fg = utils.get_highlight('TabLineSel').fg or 'none',
    tablinesel_bg = utils.get_highlight('TabLineSel').bg or 'none',
    tabline_fg = utils.get_highlight('TabLine').fg or 'none',
    red = utils.get_highlight('DiagnosticError').fg or 'none',
    yellow = utils.get_highlight('DiagnosticWarn').fg or 'none',
    green = utils.get_highlight('DiagnosticOk').fg or 'none',
    gray = utils.get_highlight('NonText').fg or 'none',
    ['function'] = utils.get_highlight('Function').fg or 'none',
    constant = utils.get_highlight('Constant').fg or 'none',
    statement = utils.get_highlight('Statement').fg or 'none',
    visual = utils.get_highlight('Visual').bg or 'none',
    diag_warn = utils.get_highlight('DiagnosticWarn').fg or 'none',
    diag_error = utils.get_highlight('DiagnosticError').fg or 'none',
    git_add = utils.get_highlight('GitSignsAdd').fg or 'none',
    git_delete = utils.get_highlight('GitSignsDelete').fg or 'none',
    git_change = utils.get_highlight('GitSignsChange').fg or 'none',
  }
end

conditions.lsp_attached = function()
  local lsp = rawget(vim, 'lsp')
  return lsp and next(lsp.get_active_clients { bufnr = 0 }) ~= nil
end

local LSPActive = {
  update = { 'LspAttach', 'LspDetach', 'VimResized', 'FileType', 'BufEnter', 'BufWritePost' },

  flexible = 1,
  {
    provider = function()
      local names = {}
      local lsp = rawget(vim, 'lsp')
      if lsp then
        for _, server in pairs(lsp.get_active_clients { bufnr = 0 }) do
          table.insert(names, server.name)
        end
      end
      local has_lint, lint = pcall(require, 'lint')
      if has_lint then
        for _, linter in ipairs(lint.linters_by_ft[vim.bo.filetype] or {}) do
          table.insert(names, linter)
        end
      end
      local conform = package.loaded.conform
      if conform then
        local formatters = conform.list_formatters(0)
        if not conform.will_fallback_lsp() then
          for _, formatter in ipairs(formatters) do
            table.insert(names, formatter.name)
          end
        end
      end
      if vim.tbl_isempty(names) then
        return ''
      else
        return ' [' .. table.concat(names, ' ') .. ']'
      end
    end,
  },
  {
    condition = conditions.lsp_attached,
    provider = ' [LSP]',
  },
  {
    condition = conditions.lsp_attached,
    provider = ' ',
  },
}

local Ruler = {
  provider = ' %P %l:%c ',
  hl = function(self)
    return { fg = 'black', bg = self:mode_color(), bold = true }
  end,
}

local Git = {
    condition = conditions.is_git_repo,

    init = function(self)
        self.status_dict = vim.b.gitsigns_status_dict
        self.has_changes = self.status_dict.added ~= 0 or self.status_dict.removed ~= 0 or self.status_dict.changed ~= 0
    end,

    hl = { fg = "orange" },


    {   -- git branch name
        provider = function(self)
            return " " .. self.status_dict.head
        end,
        hl = { bold = true }
    },
    -- You could handle delimiters, icons and counts similar to Diagnostics
    {
        condition = function(self)
            return self.has_changes
        end,
        provider = "("
    },
    {
        provider = function(self)
            local count = self.status_dict.added or 0
            return count > 0 and ("+" .. count)
        end,
        hl = { fg = setup_colors().git_add },
    },
    {
        provider = function(self)
            local count = self.status_dict.removed or 0
            return count > 0 and ("-" .. count)
        end,
        hl = { fg = setup_colors().git_delete },
    },
    {
        provider = function(self)
            local count = self.status_dict.changed or 0
            return count > 0 and ("~" .. count)
        end,
        hl = { fg = setup_colors().git_change },
    },
    {
        condition = function(self)
            return self.has_changes
        end,
        provider = ")",
    },
}

return {
  ViMode = ViMode,
  Ruler = Ruler,
  Spacer = Spacer,
  rpad = rpad,
  lpad = lpad,
  FileIcon = FileIcon,
  FileType = FileType,
  FullFileName = FullFileName,
  Diagnostics = Diagnostics,
  setup_colors = setup_colors,
  LSPActive = LSPActive,
  stl_static = stl_static,
  Git = Git,
}