local M = {}

local fallback_palette = {
  bg = '#191A1C',
  gutter = '#191A1C',
  cursor_line = '#202424',
  selection = '#08335E',
  border = '#404040',
  fg = '#BDBDBD',
  fg_bright = '#F0F0F0',
  fg_gutter = '#808080',
  comment = '#85C46C',
  keyword = '#6C95EB',
  type = '#C191FF',
  type_alt = '#E1BFFF',
  func = '#39CC9B',
  field = '#66C3CC',
  string = '#C9A26D',
  number = '#ED94C0',
  escape = '#D688D4',
  error = '#FF5647',
}

local function load_palette()
  local palette_file = vim.fn.expand '~/.config/rider-palette/palette.json'
  if vim.fn.filereadable(palette_file) == 0 then
    return fallback_palette
  end

  local ok, data = pcall(vim.json.decode, table.concat(vim.fn.readfile(palette_file), '\n'))
  if not ok or type(data) ~= 'table' or type(data.colors) ~= 'table' then
    return fallback_palette
  end

  return vim.tbl_extend('force', fallback_palette, data.colors)
end

local palette = load_palette()

M.palette = palette

function M.on_colors(colors)
  colors.bg = palette.bg
  colors.bg_dark = palette.bg
  colors.bg_float = palette.bg
  colors.bg_popup = palette.bg
  colors.bg_sidebar = palette.bg
  colors.bg_statusline = palette.bg
  colors.bg_highlight = palette.cursor_line
  colors.bg_search = palette.selection
  colors.bg_visual = palette.selection
  colors.border = palette.border
  colors.fg = palette.fg
  colors.fg_dark = palette.fg_gutter
  colors.fg_float = palette.fg
  colors.fg_gutter = palette.fg_gutter
  colors.comment = palette.comment
  colors.blue = palette.keyword
  colors.cyan = palette.field
  colors.green = palette.func
  colors.magenta = palette.type
  colors.orange = palette.string
  colors.purple = palette.type
  colors.red = palette.error
  colors.teal = palette.func
  colors.yellow = palette.string
  colors.terminal_black = palette.bg
end

local function set_many(hl, groups, spec)
  for _, group in ipairs(groups) do
    hl[group] = spec
  end
end

function M.on_highlights(hl)
  set_many(hl, {
    'Normal',
    'NormalNC',
    'SignColumn',
    'FoldColumn',
  }, { fg = palette.fg, bg = palette.bg })

  hl.EndOfBuffer = { fg = palette.bg, bg = palette.bg }
  hl.NormalFloat = { fg = palette.fg, bg = palette.bg }
  hl.FloatBorder = { fg = palette.border, bg = palette.bg }
  hl.CursorLine = { bg = palette.cursor_line }
  hl.CursorLineNr = { fg = palette.fg_bright, bg = palette.cursor_line, bold = true }
  hl.LineNr = { fg = palette.fg_gutter, bg = palette.gutter }
  hl.Folded = { fg = palette.fg_gutter, bg = palette.bg }
  hl.FoldColumn = { fg = palette.fg_gutter, bg = palette.bg }
  hl.ColorColumn = { bg = palette.cursor_line }
  hl.Visual = { bg = palette.selection }
  hl.Search = { bg = palette.selection, fg = palette.fg_bright }
  hl.IncSearch = { bg = palette.keyword, fg = palette.bg }
  hl.Pmenu = { fg = palette.fg, bg = palette.bg }
  hl.PmenuSel = { fg = palette.fg_bright, bg = palette.cursor_line }
  hl.WinSeparator = { fg = palette.border }

  set_many(hl, {
    'Comment',
    '@comment',
    '@comment.documentation',
    '@comment.todo',
    '@comment.note',
    '@comment.warning',
  }, { fg = palette.comment, italic = false })

  set_many(hl, {
    'Keyword',
    'Conditional',
    'Repeat',
    'Exception',
    'Include',
    'PreProc',
    '@keyword',
    '@keyword.function',
    '@keyword.return',
    '@keyword.repeat',
    '@keyword.conditional',
    '@keyword.exception',
    '@keyword.import',
    '@keyword.directive',
    '@keyword.operator',
    '@conditional',
    '@repeat',
    '@exception',
    '@include',
    '@module',
    '@namespace',
  }, { fg = palette.keyword })

  set_many(hl, {
    'Type',
    'StorageClass',
    'Structure',
    'Typedef',
    '@type',
    '@type.builtin',
    '@type.definition',
    '@type.qualifier',
    '@module',
    '@namespace',
    '@attribute',
    '@tag.attribute',
    '@lsp.type.type',
    '@lsp.type.class',
    '@lsp.type.interface',
    '@lsp.type.namespace',
    '@lsp.type.typeParameter',
  }, { fg = palette.type })

  set_many(hl, {
    '@constructor',
    '@lsp.type.enum',
    '@lsp.type.struct',
  }, { fg = palette.type_alt })

  set_many(hl, {
    'Function',
    '@function',
    '@function.call',
    '@function.method',
    '@function.method.call',
    '@method',
    '@constructor.lua',
    '@lsp.type.function',
    '@lsp.type.method',
  }, { fg = palette.func })

  set_many(hl, {
    'Identifier',
    '@variable',
    '@variable.member',
    '@lsp.type.variable',
  }, { fg = palette.fg_bright })

  set_many(hl, {
    'Constant',
    '@constant',
    '@field',
    '@property',
    '@lsp.type.property',
    '@lsp.type.enumMember',
  }, { fg = palette.field })

  set_many(hl, {
    'String',
    'Character',
    '@string',
    '@string.documentation',
    '@string.regex',
    '@string.special.url',
    '@lsp.type.string',
  }, { fg = palette.string })

  set_many(hl, {
    'SpecialChar',
    '@string.escape',
    '@string.special',
  }, { fg = palette.escape })

  set_many(hl, {
    'Number',
    'Float',
    '@number',
    '@number.float',
    '@lsp.type.number',
  }, { fg = palette.number })

  set_many(hl, {
    'Boolean',
    '@boolean',
    '@constant.builtin.boolean',
  }, { fg = palette.keyword })

  set_many(hl, {
    '@parameter',
    '@lsp.type.parameter',
  }, { fg = palette.fg_bright })

  set_many(hl, {
    '@variable.builtin',
    '@constant.builtin',
    '@function.builtin',
  }, { fg = palette.fg_bright })

  set_many(hl, {
    '@lsp.typemod.function.defaultLibrary',
    '@lsp.typemod.function.defaultLibrary.cs',
    '@lsp.typemod.method.defaultLibrary',
    '@lsp.typemod.method.defaultLibrary.cs',
    '@lsp.type.extensionMethod',
    '@lsp.type.extensionMethod.cs',
    '@lsp.typemod.extensionMethod.defaultLibrary',
    '@lsp.typemod.extensionMethod.defaultLibrary.cs',
  }, { fg = palette.func })

  set_many(hl, {
    '@lsp.typemod.class.defaultLibrary',
    '@lsp.typemod.class.defaultLibrary.cs',
    '@lsp.typemod.interface.defaultLibrary',
    '@lsp.typemod.interface.defaultLibrary.cs',
    '@lsp.typemod.enum.defaultLibrary',
    '@lsp.typemod.enum.defaultLibrary.cs',
    '@lsp.typemod.type.defaultLibrary',
    '@lsp.typemod.type.defaultLibrary.cs',
    '@lsp.typemod.typeParameter.defaultLibrary',
    '@lsp.typemod.typeParameter.defaultLibrary.cs',
    '@lsp.typemod.namespace.defaultLibrary',
    '@lsp.typemod.namespace.defaultLibrary.cs',
  }, { fg = palette.type })

  set_many(hl, {
    '@lsp.typemod.struct.defaultLibrary',
    '@lsp.typemod.struct.defaultLibrary.cs',
  }, { fg = palette.type_alt })

  set_many(hl, {
    '@lsp.typemod.property.defaultLibrary',
    '@lsp.typemod.property.defaultLibrary.cs',
    '@lsp.typemod.variable.defaultLibrary',
    '@lsp.typemod.variable.defaultLibrary.cs',
    '@lsp.typemod.enumMember.defaultLibrary',
    '@lsp.typemod.enumMember.defaultLibrary.cs',
  }, { fg = palette.field })

  set_many(hl, {
    'Operator',
    'Delimiter',
    '@operator',
    '@punctuation.delimiter',
    '@punctuation.bracket',
    '@punctuation.special',
  }, { fg = palette.fg })
end

return M
