local utils = require('fff.utils')

local M = {}

local function fetch_rust_checkhealth(rust_module, test_path)
  if not rust_module.health_check then
    return nil, 'health_check function not available in rust module (binary may be outdated)'
  end

  local ok, result = pcall(rust_module.health_check, test_path)
  if not ok then return nil, 'Failed to call health_check: ' .. tostring(result) end

  return result, nil
end

-- Report health for a rust-side LMDB section (frecency / query_tracker).
-- opts.populate(section, db_info) copies DB-specific counters into health_section.
-- opts.format_healthy_msg(db_info) returns the message for the healthy path;
-- the unhealthy path is handled uniformly here as a critical error.
local function report_db_health(rust_section, health_section, messages, label, opts)
  if not rust_section then return end

  health_section.initialized = rust_section.initialized
  health_section.error = rust_section.error

  if not rust_section.initialized then
    table.insert(messages, {
      level = 'info',
      msg = label .. ' not initialized (will initialize on first use)',
    })
    return
  end

  local db_info = rust_section.db_healthcheck
  if not db_info then
    if rust_section.db_healthcheck_error then
      table.insert(messages, {
        level = 'warn',
        msg = label .. ' initialized but health check failed: ' .. rust_section.db_healthcheck_error,
      })
    else
      table.insert(messages, { level = 'ok', msg = label .. ' initialized' })
    end
    return
  end

  health_section.db_path = db_info.path
  health_section.disk_size = db_info.disk_size
  health_section.healthy = db_info.healthy
  if opts.populate then opts.populate(health_section, db_info) end

  if db_info.healthy == false then
    table.insert(messages, {
      level = 'error',
      msg = string.format(
        '%s UNRESPONSIVE — CRITICAL ERROR (see logs; try removing %s/lock.mdb to unblock)',
        label,
        db_info.path or 'unknown'
      ),
    })
    return
  end

  table.insert(messages, {
    level = 'ok',
    msg = opts.format_healthy_msg(db_info),
  })
end

--- Check snacks.nvim image preview availability
--- @return table image_preview_info
local function check_image_preview()
  local ok, image = pcall(require, 'fff.file_picker.image')
  if not ok then
    return {
      available = false,
      snacks_available = false,
      snacks_image_available = false,
      terminal_supported = false,
      error = 'failed to load image module',
    }
  end

  return image.get_preview_status()
end

--- Check icon provider availability
--- @return table icon_provider_info
local function check_icon_provider()
  local ok, icons = pcall(require, 'fff.file_picker.icons')
  if not ok then return {
    available = false,
    name = nil,
    supports_directories = false,
  } end

  return icons.get_provider_info()
end

--- Run the health check and return structured results
--- @param opts? { test_path?: string } Options for health check
--- @return table health_result
function M.run(opts)
  opts = opts or {}

  local health = {
    ok = true,
    binary = {
      available = false,
      path = nil,
      error = nil,
    },
    rust = {
      version = nil,
      git = {
        available = false,
        repository_found = false,
        workdir = nil,
        libgit2_version = nil,
        error = nil,
      },
      file_picker = {
        initialized = false,
        base_path = nil,
        is_scanning = false,
        indexed_files = 0,
        error = nil,
      },
      frecency = {
        initialized = false,
        db_path = nil,
        disk_size = nil,
        entries = nil,
        healthy = nil,
        error = nil,
      },
      query_tracker = {
        initialized = false,
        db_path = nil,
        disk_size = nil,
        query_file_entries = nil,
        query_history_entries = nil,
        healthy = nil,
        error = nil,
      },
    },
    image_preview = {
      available = false,
      snacks_available = false,
      snacks_image_available = false,
      terminal_supported = false,
      error = nil,
    },
    icon_provider = {
      available = false,
      name = nil,
      supports_directories = false,
    },
    messages = {},
  }

  -- Check binary availability
  local download = require('fff.download')
  health.binary.path = download.get_binary_path()

  local binary_ok, rust_module = pcall(require, 'fff.rust')
  if not binary_ok then
    health.ok = false
    health.binary.available = false
    health.binary.error = tostring(rust_module)
    table.insert(health.messages, {
      level = 'error',
      msg = 'Binary not available: ' .. tostring(rust_module),
    })
    return health
  end

  health.binary.available = true
  table.insert(health.messages, {
    level = 'ok',
    msg = 'Binary loaded successfully from: ' .. health.binary.path,
  })

  local rust_health, rust_err = fetch_rust_checkhealth(rust_module, opts.test_path)
  if rust_health then
    health.rust.version = rust_health.version
    table.insert(health.messages, {
      level = 'ok',
      msg = 'fff.nvim version: ' .. (rust_health.version or 'unknown'),
    })

    if rust_health.git then
      health.rust.git.available = rust_health.git.available
      health.rust.git.repository_found = rust_health.git.repository_found
      health.rust.git.workdir = rust_health.git.workdir
      health.rust.git.libgit2_version = rust_health.git.libgit2_version
      health.rust.git.error = rust_health.git.error

      if rust_health.git.available then
        table.insert(health.messages, {
          level = 'ok',
          msg = 'libgit2 available (version: ' .. (rust_health.git.libgit2_version or 'unknown') .. ')',
        })

        if rust_health.git.repository_found then
          table.insert(health.messages, {
            level = 'ok',
            msg = 'Git repository found: ' .. (rust_health.git.workdir or 'unknown'),
          })
        else
          table.insert(health.messages, {
            level = 'info',
            msg = 'No git repository found in current directory'
              .. (rust_health.git.error and (': ' .. rust_health.git.error) or ''),
          })
        end
      else
        table.insert(health.messages, {
          level = 'warn',
          msg = 'libgit2 not available',
        })
      end
    end

    if rust_health.file_picker then
      health.rust.file_picker.initialized = rust_health.file_picker.initialized
      health.rust.file_picker.base_path = rust_health.file_picker.base_path
      health.rust.file_picker.is_scanning = rust_health.file_picker.is_scanning
      health.rust.file_picker.indexed_files = rust_health.file_picker.indexed_files
      health.rust.file_picker.error = rust_health.file_picker.error

      if rust_health.file_picker.initialized then
        local status = rust_health.file_picker.is_scanning and 'scanning' or 'ready'

        table.insert(health.messages, {
          level = 'ok',
          msg = string.format(
            'File picker initialized (%s, %d files indexed, base: %s)',
            status,
            rust_health.file_picker.indexed_files or 0,
            rust_health.file_picker.base_path or 'unknown'
          ),
        })
      else
        table.insert(health.messages, {
          level = 'info',
          msg = 'File picker not initialized (will initialize on first use)',
        })
      end
    end

    report_db_health(rust_health.frecency, health.rust.frecency, health.messages, 'Frecency database', {
      populate = function(section, db_info) section.entries = db_info.absolute_frecency_entries end,
      format_healthy_msg = function(db_info)
        return string.format(
          'Frecency database operational (%d entries, %s, path: %s)',
          db_info.absolute_frecency_entries or 0,
          utils.format_file_size(db_info.disk_size or 0),
          db_info.path or 'unknown'
        )
      end,
    })

    report_db_health(rust_health.query_tracker, health.rust.query_tracker, health.messages, 'Query cache database', {
      populate = function(section, db_info)
        section.query_file_entries = db_info.query_file_entries
        section.query_history_entries = db_info.query_history_entries
      end,
      format_healthy_msg = function(db_info)
        return string.format(
          'Query cache database operational (%d query-file mappings, %d history entries, %s, path: %s)',
          db_info.query_file_entries or 0,
          db_info.query_history_entries or 0,
          utils.format_file_size(db_info.disk_size or 0),
          db_info.path or 'unknown'
        )
      end,
    })
  else
    health.ok = false
    table.insert(health.messages, {
      level = 'error',
      msg = rust_err or 'Unknown error getting rust health data',
    })
    return health
  end

  local image_info = check_image_preview()
  health.image_preview.snacks_available = image_info.snacks_available
  health.image_preview.snacks_image_available = image_info.snacks_image_available
  health.image_preview.terminal_supported = image_info.terminal_supported
  health.image_preview.error = image_info.error
  health.image_preview.available = image_info.available

  if image_info.available then
    table.insert(health.messages, {
      level = 'ok',
      msg = 'Image preview available via snacks.nvim',
    })
  elseif image_info.snacks_available and image_info.snacks_image_available then
    table.insert(health.messages, {
      level = 'info',
      msg = 'Image preview not available: ' .. (image_info.error or 'terminal does not support images'),
    })
  elseif image_info.snacks_available then
    table.insert(health.messages, {
      level = 'info',
      msg = 'Image preview not available: snacks.image module not found',
    })
  else
    table.insert(health.messages, {
      level = 'info',
      msg = 'Image preview not available: snacks.nvim not installed',
    })
  end

  local icon_info = check_icon_provider()
  health.icon_provider.available = icon_info.available
  health.icon_provider.name = icon_info.name
  health.icon_provider.supports_directories = icon_info.supports_directories

  if icon_info.available then
    table.insert(health.messages, {
      level = 'ok',
      msg = 'Filetype icons available via ' .. icon_info.name,
    })
  else
    table.insert(health.messages, {
      level = 'info',
      msg = 'Filetype icons not available (install nvim-web-devicons or mini.icons)',
    })
  end

  return health
end

function M.check()
  vim.health.start('fff.nvim')

  local result = M.run()

  for _, msg in ipairs(result.messages) do
    if msg.level == 'ok' then
      vim.health.ok(msg.msg)
    elseif msg.level == 'warn' then
      vim.health.warn(msg.msg)
    elseif msg.level == 'error' then
      vim.health.error(msg.msg)
    elseif msg.level == 'info' then
      vim.health.info(msg.msg)
    end
  end

  if not result.binary.available then
    vim.health.info('To install the binary, run:')
    vim.health.info('  :lua require("fff.download").download_or_build_binary()')
    vim.health.info('Or build from source with:')
    vim.health.info('  cargo build --release')
  end
end

return M
