-- CosmicInk config for lualine
-- Author: Yeeloman
-- MIT license, see LICENSE for more details.

-- Main configuration for setting up lualine.nvim statusline plugin

-- Default Theme Colors: Define a set of base colors for your theme
local colors = {
  BG = '#16181b', -- Dark background
  FG = '#c5c4c4', -- Light foreground for contrast
  YELLOW = '#e8b75f', -- Vibrant yellow
  CYAN = '#00bcd4', -- Soft cyan
  DARKBLUE = '#2b3e50', -- Deep blue
  GREEN = '#00e676', -- Bright green
  ORANGE = '#ff7733', -- Warm orange
  VIOLET = '#7a3ba8', -- Strong violet
  MAGENTA = '#d360aa', -- Deep magenta
  BLUE = '#4f9cff', -- Light-medium blue
  RED = '#ff3344', -- Strong red
}

-- Function to get the color associated with the current mode in Vim
local function get_mode_color()
  -- Define a table mapping modes to their associated colors
  local mode_color = {
    n = colors.DARKBLUE,
    i = colors.VIOLET,
    v = colors.RED,
    [''] = colors.BLUE,
    V = colors.RED,
    c = colors.MAGENTA,
    no = colors.RED,
    s = colors.ORANGE,
    S = colors.ORANGE,
    [''] = colors.ORANGE,
    ic = colors.YELLOW,
    R = colors.ORANGE,
    Rv = colors.ORANGE,
    cv = colors.RED,
    ce = colors.RED,
    r = colors.CYAN,
    rm = colors.CYAN,
    ['r?'] = colors.CYAN,
    ['!'] = colors.RED,
    t = colors.RED,
  }
  -- Return the opposite color, or fallback to foreground color
  return mode_color[vim.fn.mode()]
end

-- Function to get the opposite color of a given mode color
local function get_opposite_color(mode_color)
  -- Define a table mapping colors to their opposite color
  local opposite_colors = {
    [colors.RED] = colors.CYAN,
    [colors.BLUE] = colors.ORANGE,
    [colors.GREEN] = colors.MAGENTA,
    [colors.MAGENTA] = colors.DARKBLUE,
    [colors.ORANGE] = colors.BLUE,
    [colors.CYAN] = colors.YELLOW,
    [colors.VIOLET] = colors.GREEN,
    [colors.YELLOW] = colors.RED,
    [colors.DARKBLUE] = colors.VIOLET,
  }
  -- Return the opposite color, or fallback to foreground color
  return opposite_colors[mode_color] or colors.FG
end

-- Function to get an animated color (randomly chosen from available colors)
local function get_animated_color(mode_color)
  -- Define a list of all available colors
  local all_colors = {
    colors.RED,
    colors.BLUE,
    colors.GREEN,
    colors.MAGENTA,
    colors.ORANGE,
    colors.CYAN,
    colors.VIOLET,
    colors.YELLOW,
    colors.DARKBLUE,
  }
  -- Create a list of possible opposite colors (excluding the current mode color)
  local possible_opposites = {}
  for _, color in ipairs(all_colors) do
    if color ~= mode_color then
      table.insert(possible_opposites, color)
    end
  end
  -- Randomly select an opposite color
  if #possible_opposites > 0 then
    local random_index = math.random(1, #possible_opposites)
    return possible_opposites[random_index]
  else
    return colors.FG -- Default to foreground color if no opposite found
  end
end

-- Function to interpolate between two colors for a smooth transition
local function interpolate_color(color1, color2, step)
  -- Blend two colors based on the given step factor (0.0 -> color1, 1.0 -> color2)
  local blend = function(c1, c2, stp)
    return math.floor(c1 + (c2 - c1) * stp)
  end
  -- Extract the RGB values of both colors (in hex)
  local r1, g1, b1 = tonumber(color1:sub(2, 3), 16), tonumber(color1:sub(4, 5), 16), tonumber(color1:sub(6, 7), 16)
  local r2, g2, b2 = tonumber(color2:sub(2, 3), 16), tonumber(color2:sub(4, 5), 16), tonumber(color2:sub(6, 7), 16)

  -- Calculate the new RGB values for the blended color
  local r = blend(r1, r2, step)
  local g = blend(g1, g2, step)
  local b = blend(b1, b2, step)

  -- Return the blended color in hex format
  return string.format('#%02X%02X%02X', r, g, b)
end

-- Function to get a middle color by interpolating between mode color and its opposite
local function get_middle_color(color_step)
  -- Set default value for color_step if not provided
  color_step = color_step or 0.5 -- If color_step is nil, default to 0.5

  local color1 = get_mode_color() -- Get the current mode color
  local color2 = get_opposite_color(color1) -- Get the opposite color

  -- Return an interpolated color between the two (based on the color_step value)
  return interpolate_color(color1, color2, color_step)
end

-- Condition: Check if the buffer is not empty
-- This checks whether the current file's name is non-empty.
-- If the file is open (i.e., has a name), it returns true, meaning the buffer is not empty.
-- local function buffer_not_empty()
-- 	return vim.fn.empty(vim.fn.expand('%:t')) ~= 1 -- 'expand('%:t')' gets the file name
-- end

-- Condition: Hide in width (only show the statusline when the window width is greater than 80)
-- This ensures that the statusline will only appear if the current window width exceeds 80 characters.
local function hide_in_width()
  return vim.fn.winwidth(0) > 80 -- 'winwidth(0)' returns the current window width
end

-- Condition: Check if the current workspace is inside a Git repository
-- This function checks if the current file is inside a Git repository by looking for a `.git` directory
-- in the current file's path. Returns true if the file is in a Git workspace.
-- local function check_git_workspace()
-- 	local filepath = vim.fn.expand('%:p:h')               -- Get the current file's directory
-- 	local gitdir = vim.fn.finddir('.git', filepath .. ';') -- Search for a `.git` directory in the file path
-- 	return gitdir and #gitdir > 0 and #gitdir < #filepath -- Returns true if a `.git` directory is found
-- end

-- -- Set random seed based on current time for randomness
math.randomseed(os.time())
-- Icon sets for random selection
local icon_sets = {
  stars = { '★', '☆', '✧', '✦', '✶', '✷', '✸', '✹' }, -- Set of star-like icons
  runes = {
    '✠',
    '⛧',
    '𖤐',
    'ᛟ',
    'ᚨ',
    'ᚱ',
    'ᚷ',
    'ᚠ',
    'ᛉ',
    'ᛊ',
    'ᛏ',
    '☠',
    '☾',
    '♰',
    '✟',
    '☽',
    '⚚',
    '🜏',
  }, -- Set of rune-like symbols
  hearts = { '❤', '♥', '♡', '❦', '❧' }, -- Set of heart-shaped icons
  waves = { '≈', '∿', '≋', '≀', '⌀', '≣', '⌇' }, -- Set of wave-like symbols
  crosses = { '☨', '✟', '♰', '♱', '⛨', '' }, -- Set of cross-like symbols
}

-- Function to select a random icon from a given set
local function get_random_icon(icons)
  return icons[math.random(#icons)] -- Returns a random icon from the set
end

-- Function to shuffle the elements in a table
local function shuffle_table(tbl)
  local n = #tbl
  while n > 1 do
    local k = math.random(n)
    tbl[n], tbl[k] = tbl[k], tbl[n] -- Swap elements
    n = n - 1 -- Decrease the size of the unsorted portion
  end
end

-- Create a list of all icon sets to allow for random selection from any set
local icon_sets_list = {}
for _, icons in pairs(icon_sets) do
  table.insert(icon_sets_list, icons) -- Add each icon set to the list
end
shuffle_table(icon_sets_list) -- Shuffle the icon sets list

-- Function to reverse the order of elements in a table
local function reverse_table(tbl)
  local reversed = {}
  for i = #tbl, 1, -1 do
    table.insert(reversed, tbl[i]) -- Insert elements in reverse order
  end
  return reversed
end

-- Create a reversed list of icon sets
local reversed_icon_sets = reverse_table(icon_sets_list)

-- Function to create a separator component based on side (left/right) and optional mode color
local function create_separator(side, use_mode_color)
  return {
    function()
      return side == 'left' and '' or '' -- Choose separator symbol based on side
    end,
    color = function()
      -- Set color based on mode or opposite color
      local color = use_mode_color and get_mode_color() or get_opposite_color(get_mode_color())
      return {
        fg = color,
      }
    end,
    padding = {
      left = 0,
    },
  }
end

-- Function to create a mode-based component (e.g., statusline)
-- with optional content, icon, and colors
local function create_mode_based_component(content, icon, color_fg, color_bg)
  return {
    content,
    icon = icon,
    color = function()
      local mode_color = get_mode_color()
      local opposite_color = get_opposite_color(mode_color)
      return {
        fg = color_fg or colors.FG,
        bg = color_bg or opposite_color,
        gui = 'bold',
      }
    end,
  }
end

-- -- Function to get the current mode indicator as a single character
local function mode()
  -- Map of modes to their respective shorthand indicators
  local mode_map = {
    n = 'N', -- Normal mode
    i = 'I', -- Insert mode
    v = 'V', -- Visual mode
    [''] = 'V', -- Visual block mode
    V = 'V', -- Visual line mode
    c = 'C', -- Command-line mode
    no = 'N', -- NInsert mode
    s = 'S', -- Select mode
    S = 'S', -- Select line mode
    ic = 'I', -- Insert mode (completion)
    R = 'R', -- Replace mode
    Rv = 'R', -- Virtual Replace mode
    cv = 'C', -- Command-line mode
    ce = 'C', -- Ex mode
    r = 'R', -- Prompt mode
    rm = 'M', -- More mode
    ['r?'] = '?', -- Confirm mode
    ['!'] = '!', -- Shell mode
    t = 'T', -- Terminal mode
  }
  -- Return the mode shorthand or [UNKNOWN] if no match
  return mode_map[vim.fn.mode()] or '[UNKNOWN]'
end

-- Config
local config = {
  options = {
    component_separators = '',
    section_separators = '',
    theme = {
      normal = {
        c = {
          fg = colors.FG,
          bg = colors.BG,
        },
      },
      inactive = {
        c = {
          fg = colors.FG,
          bg = colors.BG,
        },
      }, -- Simplified inactive theme
    },
    disabled_filetypes = {
      'neo-tree',
      'undotree',
      'sagaoutline',
      'diff',
    },
  },
  sections = {
    lualine_a = {},
    lualine_b = {},
    lualine_c = {},
    lualine_x = {},
    lualine_y = {},
    lualine_z = {},
  },
  inactive_sections = {
    lualine_a = {},
    lualine_b = {},
    lualine_c = {
      {

        'location',
        color = function()
          return {
            fg = colors.FG,
            gui = 'bold',
          }
        end,
      },
    },
    lualine_x = {
      {
        'filename',
        color = function()
          return {
            fg = colors.FG,
            gui = 'bold,italic',
          }
        end,
      },
    },
    lualine_y = {},
    lualine_z = {},
  },
}

-- Helper functions
local function ins_left(component)
  table.insert(config.sections.lualine_c, component)
end

local function ins_right(component)
  table.insert(config.sections.lualine_x, component)
end

-- LEFT
ins_left {
  mode,
  color = function()
    local mode_color = get_mode_color()
    return {
      fg = colors.BG,
      bg = mode_color,
      gui = 'bold',
    }
  end,
  padding = { left = 1, right = 1 },
}

ins_left(create_separator('left', true))

ins_left {
  function()
    return vim.fn.fnamemodify(vim.fn.getcwd(), ':t')
  end,
  icon = ' ',
  color = function()
    local virtual_env = vim.env.VIRTUAL_ENV
    if virtual_env then
      return {
        fg = get_mode_color(),
        gui = 'bold,strikethrough',
      }
    else
      return {
        fg = get_mode_color(),
        gui = 'bold',
      }
    end
  end,
}

ins_left(create_separator('right'))

ins_left(create_mode_based_component('filename', nil, colors.BG))

ins_left(create_separator('left'))

ins_left {
  function()
    return ''
  end,
  color = function()
    return {
      fg = get_middle_color(),
    }
  end,
  cond = hide_in_width,
}

ins_left {
  function()
    local git_status = vim.b.gitsigns_status_dict
    if git_status then
      return string.format('+%d ~%d -%d', git_status.added or 0, git_status.changed or 0, git_status.removed or 0)
    end
    return ''
  end,
  -- icon = '󰊢 ',
  color = {
    fg = colors.YELLOW,
    gui = 'bold',
  },
  cond = hide_in_width,
}

for _, icons in pairs(icon_sets_list) do
  ins_left {
    function()
      return get_random_icon(icons)
    end,
    color = function()
      return {
        fg = get_animated_color(),
      }
    end,
    cond = hide_in_width,
  }
end

ins_left {
  'searchcount',
  color = {
    fg = colors.GREEN,
    gui = 'bold',
  },
}

-- RIGHT
ins_right {
  function()
    local reg = vim.fn.reg_recording()
    return reg ~= '' and '[' .. reg .. ']' or ''
  end,
  color = {
    fg = '#ff3344',
    gui = 'bold',
  },
  cond = function()
    return vim.fn.reg_recording() ~= ''
  end,
}

ins_right {
  'selectioncount',
  color = {
    fg = colors.GREEN,
    gui = 'bold',
  },
}

for _, icons in ipairs(reversed_icon_sets) do
  ins_right {
    function()
      return get_random_icon(icons)
    end,
    color = function()
      return {
        fg = get_animated_color(),
      }
    end,
    cond = hide_in_width,
  }
end

ins_right {
  function()
    local msg = 'No Active Lsp'
    local buf_ft = vim.api.nvim_buf_get_option(0, 'filetype')
    local clients = vim.lsp.get_clients()
    if next(clients) == nil then
      return msg
    end
    local lsp_short_names = {
      pyright = 'py',
      tsserver = 'ts',
      rust_analyzer = 'rs',
      lua_ls = 'lua',
      clangd = 'c++',
      bashls = 'sh',
      jsonls = 'json',
      html = 'html',
      cssls = 'css',
      tailwindcss = 'tw',
      dockerls = 'docker',
      sqlls = 'sql',
      yamlls = 'yml',
    }
    for _, client in ipairs(clients) do
      local filetypes = client.config.filetypes
      if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then
        return lsp_short_names[client.name] or client.name:sub(1, 2)
      end
    end
    return msg
  end,
  icon = ' ',
  color = {
    fg = colors.YELLOW,
    gui = 'bold',
  },
}

ins_right {
  function()
    return ''
  end,
  color = function()
    return { fg = get_middle_color() }
  end,
  cond = hide_in_width,
}

ins_right(create_separator('right'))

ins_right(create_mode_based_component('location', nil, colors.BG))

ins_right(create_separator('left'))

ins_right {
  'branch',
  icon = ' ',
  --[[ Truncates and formats Git branch names for display in lualine:
    First segment: Uppercase, truncated to 1 character.
    Middle segments: Lowercase, truncated to 1 character.
    Last segment: Unchanged.
    Separator: › between truncated segments and the last segment.

    Example Input/Output:
		Branch										Name	Output
		backend/setup/tailwind		Bs›tailwind
		feature/add-ui						Fa›add-ui
		main											main
	]]
  fmt = function(branch)
    if branch == '' or branch == nil then
      return 'No Repo'
    end

    -- Function to truncate a segment to a specified length
    local function truncate_segment(segment, max_length)
      if #segment > max_length then
        return segment:sub(1, max_length)
      end
      return segment
    end

    -- Split the branch name by '/'
    local segments = {}
    for segment in branch:gmatch('[^/]+') do
      table.insert(segments, segment)
    end

    -- Truncate all segments except the last one
    for i = 1, #segments - 1 do
      segments[i] = truncate_segment(segments[i], 1) -- Truncate to 1 character
    end

    -- If there's only one segment (no '/'), return it as-is
    if #segments == 1 then
      return segments[1]
    end

    -- Capitalize the first segment and lowercase the rest (except the last one)
    segments[1] = segments[1]:upper() -- First segment uppercase
    for i = 2, #segments - 1 do
      segments[i] = segments[i]:lower() -- Other segments lowercase
    end

    -- Combine the first segments with no separator and add '›' before the last segment
    local truncated_branch = table.concat(segments, '', 1, #segments - 1) .. '›' .. segments[#segments]

    -- Ensure the final result doesn't exceed a maximum length
    local max_total_length = 15
    if #truncated_branch > max_total_length then
      truncated_branch = truncated_branch:sub(1, max_total_length) .. '…'
    end

    return truncated_branch
  end,
  color = function()
    local mode_color = get_mode_color()
    return {
      fg = mode_color,
      gui = 'bold',
    }
  end,
}

ins_right(create_separator('right'))

ins_right(create_mode_based_component('progress', nil, colors.BG))

require('lualine').setup(config)
