# 多机 NixOS 配置任务面板 —— `just --list` 查看全部命令
# 在仓库目录内运行（just 向上查找）；也可 just -f ~/nixos-config/Justfile <命令>
#
# 多机约定：flake 属性名 = 主机名。不带参数时操作"当前机器"（取 `hostname`），
# 操作别的机器：just rebuild alpha / just build alpha / just diff alpha。

repo := justfile_directory()

# 部署到当前机器（首次装机主机名还没改过来时：just rebuild alpha force=1）
rebuild host=`hostname` force="":
    #!/usr/bin/env bash
    set -euo pipefail
    if [[ "{{host}}" != "$(hostname)" && -z "{{force}}" ]]; then
        echo "⚠️  目标主机 {{host}} ≠ 当前主机 $(hostname)" >&2
        echo "   nixos-rebuild switch 只切换当前这台机器，跨机部署请 ssh 到目标机器再执行。" >&2
        echo "   首次装机（本机 hostname 还没设成 {{host}}）确实要本地部署：just rebuild {{host}} force=1" >&2
        exit 1
    fi
    sudo nixos-rebuild switch --flake {{repo}}#{{host}}

# 脱离当前会话重建（dsh-web 自身要被重启时用这个，否则 nixos-rebuild 会被连带杀掉）
rebuild-detached host=`hostname`:
    #!/usr/bin/env bash
    set -euo pipefail
    unit="nrs-$(date +%s)"
    sudo systemd-run --collect --no-block --unit="$unit" \
        --property=StandardOutput=journal --property=StandardError=journal \
        --property=Environment=PATH=/run/current-system/sw/bin:/run/wrappers/bin:/nix/var/nix/profiles/default/bin:/usr/bin:/bin \
        /run/current-system/sw/bin/nixos-rebuild switch --flake {{repo}}#{{host}}
    echo "✅ 已在后台切换（unit: $unit）—— dsh-web 会被重启，页面刷新后可继续"
    echo "   跟进：journalctl -u $unit -f"

# 构建并与当前系统对比差异（不切换，安全预览；只对当前机器有意义）
diff host=`hostname`:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{repo}}
    nixos-rebuild build --flake .#{{host}}
    nix store diff-closures /run/current-system ./result

# 只构建不部署（给别的机器预构建/验证评估，不动当前系统）
build host=`hostname`:
    nixos-rebuild build --flake {{repo}}#{{host}}

# 列出本仓库登记的所有主机
hosts:
    @nix eval --raw {{repo}}#nixosConfigurations --apply 'cfg: builtins.concatStringsSep "\n" (builtins.attrNames cfg) + "\n"'

# 更新 flake 输入（nixpkgs / home-manager / metacubexd）
update:
    nix flake update --flake {{repo}}

# 更新输入并部署（可带主机名：just upgrade alpha）
upgrade host=`hostname`: (update) (rebuild host)

# 垃圾回收：删除 14 天前的旧代（系统 + 用户；保留回滚窗口）
gc:
    sudo nix-collect-garbage --delete-older-than 14d
    nix-collect-garbage --delete-older-than 14d

# 查看系统代历史（回滚 = 重启时在启动菜单选旧代）
history:
    sudo nix-env --list-generations --profile /nix/var/nix/profiles/system | tail -10

# ---- 密钥（sops-nix）--------------------------------------------------------
# sops / age-keygen 优先用系统 PATH 里的；本次改动还没部署时自动回退到 flake 的 devShell。

# 生成你的管理密钥（编辑 secret 用；只需一次，务必备份；已存在则不动）
secrets-admin-key:
    #!/usr/bin/env bash
    set -euo pipefail
    dir=${XDG_CONFIG_HOME:-$HOME/.config}/sops/age
    key=$dir/keys.txt
    mkdir -p "$dir"
    chmod 700 "$dir"   # 私钥目录只给自己
    if [[ -f $key ]]; then
        echo "已存在：$key（不动）"
    else
        if command -v age-keygen >/dev/null 2>&1; then
            age-keygen -o "$key"
        else
            nix develop {{repo}} -c age-keygen -o "$key"
        fi
        chmod 600 "$key"
    fi
    echo "公钥（填进 .sops.yaml 的 &admin）："
    if command -v age-keygen >/dev/null 2>&1; then
        age-keygen -y "$key"
    else
        nix develop {{repo}} -c age-keygen -y "$key"
    fi

# 生成机器密钥（首次部署前也能用；已存在则不动）
secrets-key-gen:
    #!/usr/bin/env bash
    set -euo pipefail
    key=/var/lib/sops-nix/key.txt
    if [[ -f $key ]]; then
        echo "已存在：$key（不动）"
        exit 0
    fi
    tmpdir=$(mktemp -d)
    trap 'rm -rf "$tmpdir"' EXIT
    tmp=$tmpdir/key.txt
    if command -v age-keygen >/dev/null 2>&1; then
        age-keygen -o "$tmp"
    else
        nix develop {{repo}} -c age-keygen -o "$tmp"
    fi
    sudo mkdir -p /var/lib/sops-nix
    sudo install -m 600 -o root -g root "$tmp" "$key"
    echo "✅ 已生成 $key，公钥如下（填进 .sops.yaml）："
    just -f {{repo}}/Justfile secrets-key

# 打印本机 age 公钥（填进 .sops.yaml 用）
secrets-key:
    #!/usr/bin/env bash
    set -euo pipefail
    key=/var/lib/sops-nix/key.txt
    if [[ ! -f $key ]]; then
        echo "❌ $key 不存在，先跑：just secrets-key-gen" >&2
        exit 1
    fi
    # 经 sudo cat 读取，避免 root 的 PATH 里没有 age-keygen
    if command -v age-keygen >/dev/null 2>&1; then
        sudo cat "$key" | age-keygen -y /dev/stdin
    else
        sudo cat "$key" | nix develop {{repo}} -c age-keygen -y /dev/stdin
    fi

# 初始化 secrets/：从模板生成并原地加密（需要先填好 .sops.yaml 的真实公钥）
secrets-init:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{repo}}
    sops_() { if command -v sops >/dev/null 2>&1; then sops "$@"; else nix develop {{repo}} -c sops "$@"; fi; }
    if grep -vE '^[[:space:]]*#' .sops.yaml | grep -q "REPLACE_WITH"; then
        echo "❌ .sops.yaml 里还有占位公钥（未注释的行），先填真实 age 公钥（见 secrets/README.md）" >&2
        exit 1
    fi
    for name in llm.env users.yaml; do
        if [ -e "secrets/$name" ]; then
            echo "跳过已存在：secrets/$name"
            continue
        fi
        cp "secrets/$name.example" "secrets/$name"
        sops_ -e -i "secrets/$name"
        echo "✅ 已加密 secrets/$name"
    done
    echo
    echo "mihomo 配置迁移：just secrets-import-mihomo"
    echo
    echo "⚠️  secrets/users.yaml 里的 hash 还是占位值，编辑成真实 hash 之前不要 just rebuild"
    echo "    （否则 atlas 的密码会变成一个无效 hash）。生成 hash：echo '密码' | mkpasswd -m yescrypt -s"
    echo
    echo "完成后把密文加入 git（Nix 只看得见被跟踪的文件）：git add secrets/"

# 把现有 /etc/mihomo/config.yaml 加密迁移到 secrets/mihomo.yaml（保留注释与规则）
secrets-import-mihomo force="":
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{repo}}
    sops_() { if command -v sops >/dev/null 2>&1; then sops "$@"; else nix develop {{repo}} -c sops "$@"; fi; }
    src=/etc/mihomo/config.yaml
    dst=secrets/mihomo.yaml
    if [[ -e $dst && -z "{{force}}" ]]; then
        echo "⚠️  $dst 已存在；要覆盖请：just secrets-import-mihomo force=1" >&2
        exit 1
    fi
    tmp=$(mktemp --suffix=.yaml)
    out=$(mktemp --suffix=.yaml)
    trap 'rm -f "$tmp" "$out"' EXIT
    if [[ -r $src ]]; then
        cat "$src" > "$tmp"
    else
        echo "→ 读取 $src 需要 sudo"
        sudo cat "$src" > "$tmp"   # 重定向由当前用户执行，$tmp 仍属自己
    fi
    # --filename-override：输入在 /tmp，路径里没有 secrets/，靠它匹配 .sops.yaml 的规则
    sops_ -e --filename-override "$dst" "$tmp" > "$out"
    mv "$out" "$dst"
    echo "✅ 已加密迁移到 $dst"
    echo "   下一步：git add $dst && just rebuild"

# 编辑一个 secret（解密 → 编辑器 → 保存时重新加密）：just secrets-edit llm.env
secrets-edit file:
    #!/usr/bin/env bash
    set -euo pipefail
    if command -v sops >/dev/null 2>&1; then
        sops {{repo}}/secrets/{{file}}
    else
        nix develop {{repo}} -c sops {{repo}}/secrets/{{file}}
    fi

# 新建一个 secret（按 .sops.yaml 的 creation_rules 加密）：just secrets-new k8s.yaml
secrets-new file:
    #!/usr/bin/env bash
    set -euo pipefail
    if command -v sops >/dev/null 2>&1; then
        sops {{repo}}/secrets/{{file}}
    else
        nix develop {{repo}} -c sops {{repo}}/secrets/{{file}}
    fi

# 改了 .sops.yaml 的接收者后重刷所有密文（新增机器必做）
secrets-updatekeys:
    #!/usr/bin/env bash
    set -euo pipefail
    shopt -s nullglob
    sops_() { if command -v sops >/dev/null 2>&1; then sops "$@"; else nix develop {{repo}} -c sops "$@"; fi; }
    files=({{repo}}/secrets/*.yaml {{repo}}/secrets/*.env)
    if [[ ${#files[@]} -eq 0 ]]; then
        echo "secrets/ 下还没有加密文件（先跑 just secrets-init）"
        exit 0
    fi
    for f in "${files[@]}"; do
        echo "→ $(basename "$f")"
        sops_ updatekeys -y "$f"
    done

# 授权一台新机器解密：just secrets-authorize alpha age1xxxx...
# 写 .sops.yaml → 重刷所有密文 → 提交 → push（全自动，无需手改文件）
secrets-authorize host pubkey:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{repo}}
    host="{{host}}"
    pub="{{pubkey}}"
    age_() { if command -v age >/dev/null 2>&1; then age "$@"; else nix develop {{repo}} -c age "$@"; fi; }
    [[ $pub =~ ^age1[0-9a-z]{50,}$ ]] || { echo "❌ 公钥格式不对：$pub" >&2; exit 1; }
    # 用 age 真正校验一次（Bech32 校验和），避免把抄错的公钥写进 .sops.yaml
    echo test | age_ -r "$pub" -o /dev/null 2>/dev/null || {
        echo "❌ age 校验失败：这个公钥不合法（可能抄错/被截断）：$pub" >&2
        exit 1
    }
    if grep -q "$pub" .sops.yaml; then
        echo "公钥已在 .sops.yaml 里，跳过插入"
    else
        awk -v h="$host" -v p="$pub" '
          { lines[NR] = $0 }
          /^[[:space:]]+- &/ { last_key = NR }
          /^[[:space:]]+- \*/ { last_ref = NR }
          END {
            for (i = 1; i <= NR; i++) {
              print lines[i]
              if (i == last_key) print "  - &" h " " p
              if (i == last_ref) print "          - *" h
            }
          }
        ' .sops.yaml > .sops.yaml.tmp && mv .sops.yaml.tmp .sops.yaml
        echo "✅ 已把 $host 加进 .sops.yaml（keys + creation_rules）"
    fi
    just -f {{repo}}/Justfile secrets-updatekeys
    git add .sops.yaml secrets/
    if git diff --cached --quiet; then
        echo "没有需要提交的改动"
    else
        git commit -q -m "授权 $host 解密"
        git push
        echo "✅ 已提交并 push —— 新机器现在可以解开了"
    fi

# 提交前自检：secrets/ 下不能出现明文
secrets-check:
    #!/usr/bin/env bash
    set -euo pipefail
    shopt -s nullglob
    rc=0
    for f in {{repo}}/secrets/*.yaml {{repo}}/secrets/*.env; do
        if grep -qE 'ENC\[AES256|^sops:' "$f"; then
            echo "✅ $(basename "$f") 是密文"
        else
            echo "❌ $(basename "$f") 看起来是明文（缺 sops 元数据），不要提交！" >&2
            rc=1
        fi
    done
    exit $rc

# ---- SSH 远程登录 -----------------------------------------------------------
# 公钥清单在仓库根的 keys.nix（modules/nixos/ssh.nix 把清单里所有公钥授权到所有机器）。
# ssh-keygen / gh 优先用系统 PATH 里的；还没部署过本仓库的机器自动回退到 nix shell。

# 打印本机 SSH 公钥（~/.ssh/id_ed25519 不存在就生成；无口令，权限 600）
ssh-key:
    #!/usr/bin/env bash
    set -euo pipefail
    keygen() { if command -v ssh-keygen >/dev/null 2>&1; then ssh-keygen "$@"; else nix shell nixpkgs#openssh -c ssh-keygen "$@"; fi; }
    priv=$HOME/.ssh/id_ed25519
    mkdir -p "$HOME/.ssh"
    chmod 700 "$HOME/.ssh"
    if [[ ! -f $priv ]]; then
        keygen -t ed25519 -f "$priv" -C "$(whoami)@$(hostname)" -N "" -q
        chmod 600 "$priv"
        echo "✅ 已生成 $priv（无口令）" >&2
    fi
    cat "$priv.pub"

# 把本机公钥登记进 keys.nix：生成（若缺）→ 写入 → 提交 → push；随后重建即生效
ssh-publish:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{repo}}
    host=$(hostname)
    key=$(just -f {{repo}}/Justfile ssh-key | tail -n1)
    bash {{repo}}/scripts/ssh-keys-add.sh "$host" "$key"
    git add keys.nix
    if git diff --cached --quiet; then
        echo "keys.nix 无改动（公钥已经登记过）"
    else
        git commit -q -m "SSH：登记 $host 的公钥"
        git push
        echo "✅ 已提交并 push —— 各机器 just rebuild 后即可 ssh 到本机"
    fi
    echo "   另外可把这把公钥注册到 GitHub（ssh 方式的 git 操作）：just ssh-github"

# 代登记别的主机的公钥（对端还没法 push 时用）：just ssh-authorize alpha "ssh-ed25519 AAAA... atlas@alpha"
ssh-authorize host pubkey:
    #!/usr/bin/env bash
    set -euo pipefail
    cd {{repo}}
    bash {{repo}}/scripts/ssh-keys-add.sh "{{host}}" "{{pubkey}}"
    git add keys.nix
    if git diff --cached --quiet; then
        echo "keys.nix 无改动（公钥已经登记过）"
    else
        git commit -q -m "SSH：登记 {{host}} 的公钥"
        git push
        echo "✅ 已提交并 push —— 各机器 just rebuild 后 {{host}} 的公钥即可登录"
    fi

# 把本机公钥注册到 GitHub（ssh 方式 clone/push 用；token 需 admin:public_key 权限）
ssh-github:
    #!/usr/bin/env bash
    set -euo pipefail
    host=$(hostname)
    key=$(just -f {{repo}}/Justfile ssh-key | tail -n1)
    gh_() { if command -v gh >/dev/null 2>&1; then gh "$@"; else nix shell nixpkgs#gh -c gh "$@"; fi; }
    tmp=$(mktemp)
    trap 'rm -f "$tmp"' EXIT
    printf '%s\n' "$key" > "$tmp"
    if ! out=$(gh_ ssh-key add "$tmp" --title "$host" 2>&1); then
        printf '%s\n' "$out" >&2
        echo "❌ 注册失败。若报 admin:public_key（token 权限不够），先跑：" >&2
        echo "   gh auth refresh -h github.com -s admin:public_key   # 浏览器里输一次性验证码" >&2
        exit 1
    fi
    printf '%s\n' "$out"
    echo "✅ 已把 $host 的公钥注册到 GitHub"

# 打包仓库快照（只含 git 跟踪的文件），用于挂到静态资源的引导脚本
bundle:
    bash {{repo}}/scripts/make-bundle.sh {{repo}}/nixos-config.tar.gz

# 组装静态资源发布目录 dist/（bootstrap.sh + nixos-config.tar.gz + index.html）
dist:
    bash {{repo}}/scripts/make-pages-dist.sh {{repo}}/dist

# 检查 flake 评估是否正常（所有主机）
check:
    nix flake check {{repo}} --no-build

# 格式化所有 .nix 文件（nixfmt，RFC 风格）
fmt:
    nixfmt $(git -C {{repo}} ls-files '*.nix')

# 静态检查：statix 反模式 + deadnix 死代码（工具在 devShell 里）
lint:
    statix check {{repo}}
    deadnix --fail {{repo}}
