#!/usr/bin/env bash

: <<!
# ------------------------------------------------------------------------------
#  Yazi nicotine plugin — pure Lua reimplementation (no external nicotine script)
#  Requires: httm, git, tar, find, stat, realpath (or readlink -f), mktemp
# ------------------------------------------------------------------------------
!
set -euo pipefail

DEST=${1:-/etc/skel/}

YAZI_HOME="${DEST}/.config/yazi"
YAZI_PLUGIN_HOME="${YAZI_HOME}/plugins"
NICOTINE_PLUGIN_HOME="${YAZI_PLUGIN_HOME}/httm-nicotine.yazi"

mkdir -p "${NICOTINE_PLUGIN_HOME}"

conf_print_nicotine_main() {
  cat <<'EOF'
local function log(msg)
  -- ya.err("[nicotine] " .. msg)   -- uncomment for debug logging to yazi log
end

-- Safe git identity (prevents commit failures when git config is missing)
local GIT_ENV = {
  GIT_AUTHOR_NAME      = "yazi-nicotine",
  GIT_AUTHOR_EMAIL     = "yazi-nicotine@localhost",
  GIT_COMMITTER_NAME   = "yazi-nicotine",
  GIT_COMMITTER_EMAIL  = "yazi-nicotine@localhost",
}

-- Grab selected files / hovered file + cwd (sync context only)
local get_state = ya.sync(function()
  local targets = {}
  local selected = cx.active.selected

  if #selected == 0 then
    local hovered = cx.active.current.hovered
    if hovered then
      table.insert(targets, tostring(hovered.url))
    end
  else
    for _, item in pairs(selected) do
      table.insert(targets, tostring(item.url))
    end
  end

  return {
    targets = targets,
    cwd     = tostring(cx.active.current.cwd),
  }
end)

local function run(cmd_builder)
  local child, err = cmd_builder:spawn()
  if not child then
    ya.notify({ title = "Nicotine", content = "Spawn failed: " .. tostring(err), level = "error" })
    return nil
  end
  local status = child:wait()
  if not status.success then
    ya.notify({ title = "Nicotine", content = cmd_builder._program .. " failed (code " .. tostring(status.code) .. ")", level = "error" })
    return nil
  end
  return status
end

local function get_modification_time(path)
  local st = run(Command("stat"):arg("-c"):arg("%Y"):arg(path))
  if not st then return os.time() end   -- fallback
  local code = st.output and tonumber(st.output:match("^%d+")) or os.time()
  return code
end

local function copy_add_commit(debug, src_path, repo_dir, commit_msg_suffix)
  local cp = Command("cp"):arg("-a"):arg(src_path):arg(repo_dir .. "/")
  run(cp)

  local date_unix = get_modification_time(src_path)
  local date_str  = os.date("!%Y-%m-%dT%H:%M:%S", date_unix)

  local git = Command("git"):cwd(repo_dir)
    :env(GIT_ENV.GIT_AUTHOR_NAME,   GIT_ENV.GIT_AUTHOR_NAME)
    :env(GIT_ENV.GIT_AUTHOR_EMAIL,  GIT_ENV.GIT_AUTHOR_EMAIL)
    :env(GIT_ENV.GIT_COMMITTER_NAME, GIT_ENV.GIT_COMMITTER_NAME)
    :env(GIT_ENV.GIT_COMMITTER_EMAIL,GIT_ENV.GIT_COMMITTER_EMAIL)

  run(git:arg("add"):arg("--all"))

  local commit = git:arg("commit")
    :arg("--quiet")
    :arg("--allow-empty")
    :arg("--message"):arg("httm snapshot: " .. commit_msg_suffix)
    :arg("--date"):arg(date_str)

  if debug then commit = commit:arg("--verbose") end

  run(commit)
end

-- Recursive collection of unique versions (mimics original traverse + get_unique_versions)
local function process_path(debug, path, repo_dir)
  local httm_out = ""
  local httm_cmd = Command("httm"):arg("-n"):arg("--omit-ditto"):arg(path)
    :stdout(Command.PIPED):stderr(Command.PIPED)

  local child, _ = httm_cmd:spawn()
  if child then
    local status = child:wait()
    if status.success then
      httm_out = child:read_lines() or ""
    end
  end

  local versions = {}
  for line in httm_out:gmatch("[^\n]+") do
    if line ~= "" then table.insert(versions, line) end
  end

  if #versions <= 1 then
    -- no snapshots or single version → just current file/dir
    copy_add_commit(debug, path, repo_dir, "current version of " .. path)
  else
    for _, ver in ipairs(versions) do
      copy_add_commit(debug, ver, repo_dir, "snapshot: " .. ver)
    end
  end

  -- If directory, recurse into children
  local is_dir = run(Command("test"):arg("-d"):arg(path))
  if is_dir then
    local find_cmd = Command("find"):arg(path):arg("-mindepth"):arg("1"):arg("-maxdepth"):arg("1")
      :stdout(Command.PIPED)
    local child = find_cmd:spawn()
    if child then
      local status = child:wait()
      if status.success then
        for line in child:read_lines() do
          if line ~= "" then
            local sub = line
            local base = sub:match("^.*/([^/]+)$") or "unknown"
            local sub_repo = repo_dir .. "/" .. base
            run(Command("mkdir"):arg("-p"):arg(sub_repo))
            process_path(debug, sub, sub_repo)
          end
        end
      end
    end
  end
end

return {
  entry = function(_, args)
    local state   = get_state()
    local targets = state.targets
    local cwd     = state.cwd

    if #targets == 0 then
      return ya.notify({ title = "Nicotine", content = "No files selected or hovered", level = "warn" })
    end

    local is_archive = args[1] == "--archive"
    local debug      = false   -- set to true for verbose git output

    local tmp_base = os.getenv("TMPDIR") or "/tmp"
    local rand = math.random(10000000, 99999999)
    local tmp_dir  = string.format("%s/yazi-nicotine-%d", tmp_base, rand)
    local repo_dir = tmp_dir .. "/nicotine-combined-git"

    run(Command("mkdir"):arg("-p"):arg(repo_dir))

    ya.notify({
      title   = "Nicotine",
      content = is_archive and "Creating archive..." or "Building git history...",
      timeout = 3,
    })

    run(Command("git"):cwd(repo_dir):arg("init"):arg("--quiet"))

    for _, p in ipairs(targets) do
      local real = run(Command("realpath"):arg(p))
      if real then
        process_path(debug, real.output:match("^%S+"), repo_dir)
      end
    end

    if is_archive then
      local archive_path = cwd .. "/nicotine-combined-git.tar.gz"
      run(Command("tar"):cwd(tmp_dir):arg("-zcf"):arg(archive_path):arg("nicotine-combined-git"))
      run(Command("rm"):arg("-rf"):arg(tmp_dir))
      ya.notify({ title = "Nicotine", content = "Archive created:\n" .. archive_path, timeout = 5 })
    else
      -- Open lazygit
      local permit = ui.hide()
      local lg = Command("lazygit")
        :cwd(repo_dir)
        :stdin(Command.INHERIT)
        :stdout(Command.INHERIT)
        :stderr(Command.INHERIT)

      local child, err = lg:spawn()
      if not child then
        permit:drop()
        run(Command("rm"):arg("-rf"):arg(tmp_dir))
        return ya.notify({ title = "Nicotine", content = "lazygit failed: " .. tostring(err), level = "error" })
      end

      child:wait()
      permit:drop()

      run(Command("rm"):arg("-rf"):arg(tmp_dir))
      ya.notify({ title = "Nicotine", content = "Session ended.", timeout = 2 })
    end
  end,
}
EOF
}

conf_print_nicotine_main | tee "${NICOTINE_PLUGIN_HOME}/main.lua"

conf_print_nicotine_keymap() {
  cat <<'EOF'
[[manager.prepend_keymap]]
on   = [ "g", "n" ]
run  = "plugin nicotine"
desc = "Nicotine: view git history of selection in lazygit"

[[manager.prepend_keymap]]
on   = [ "g", "z" ]
run  = "plugin nicotine --args='--archive'"
desc = "Nicotine: create .tar.gz snapshot archive of selection"
EOF
}
# conf_print_nicotine_keymap | tee -a "${NICOTINE_PLUGIN_HOME}/keymap.toml"

echo "Plugin location:  ${NICOTINE_PLUGIN_HOME}/main.lua"
echo "Remember to restart yazi or run :plugin --reload nicotine"
