#!/bin/bash

#######################################################
# LVM自动配置脚本 - Debian 13
# 功能: 清空非系统硬盘并创建LVM卷组
# 警告: 此脚本会删除所有非系统硬盘上的数据!
#######################################################

set -e  # 遇到错误立即退出
shopt -s extglob  # 启用扩展模式匹配

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 无颜色

# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# 检查是否为root用户
if [ "$EUID" -ne 0 ]; then 
    log_error "请使用root权限运行此脚本"
    exit 1
fi

# 检查必要的工具
log_info "检查必要的工具..."
for cmd in lsblk parted pvcreate vgcreate; do
    if ! command -v $cmd &> /dev/null; then
        log_error "$cmd 未找到，正在安装lvm2..."
        apt-get update && apt-get install -y lvm2 parted
        break
    fi
done

# 获取系统硬盘
log_info "识别系统硬盘..."
# 查找根分区所在的硬盘
ROOT_PARTITION=$(df / | tail -1 | awk '{print $1}')

# 处理不同类型的根分区
if [[ "$ROOT_PARTITION" == /dev/mapper/* ]]; then
    # LVM情况
    log_info "检测到LVM根分区: $ROOT_PARTITION"
    # 获取LVM使用的物理卷
    PV_DEVICES=$(pvs --noheadings -o pv_name 2>/dev/null | tr -d ' ')
    if [ -z "$PV_DEVICES" ]; then
        log_error "无法获取LVM物理卷信息"
        exit 1
    fi
    # 获取物理卷对应的硬盘
    SYSTEM_DISKS=()
    for pv in $PV_DEVICES; do
        # 去除分区号得到硬盘设备
        disk=$(echo "$pv" | sed 's/[0-9]*$//' | sed 's/p$//')
        SYSTEM_DISKS+=("$disk")
    done
elif [[ "$ROOT_PARTITION" == /dev/md* ]]; then
    # RAID情况
    log_info "检测到RAID根分区: $ROOT_PARTITION"
    RAID_DEVICES=$(mdadm --detail "$ROOT_PARTITION" 2>/dev/null | grep -oP '/dev/[a-z]+' | sort -u)
    SYSTEM_DISKS=()
    for dev in $RAID_DEVICES; do
        disk=$(echo "$dev" | sed 's/[0-9]*$//' | sed 's/p$//')
        SYSTEM_DISKS+=("$disk")
    done
else
    # 普通分区
    log_info "检测到普通根分区: $ROOT_PARTITION"
    SYSTEM_DISK=$(echo "$ROOT_PARTITION" | sed 's/[0-9]*$//' | sed 's/p$//')
    SYSTEM_DISKS=("$SYSTEM_DISK")
fi

# 显示识别的系统硬盘
log_info "系统硬盘识别为:"
for disk in "${SYSTEM_DISKS[@]}"; do
    if [ ! -b "$disk" ]; then
        log_error "设备不存在: $disk"
        exit 1
    fi
    log_info "  - $disk"
done

# 获取所有块设备(排除loop设备、分区和系统盘)
log_info "扫描可用硬盘..."

# 检查并显示RAID设备
log_info "检查RAID设备..."
RAID_DEVICES=$(lsblk -dn -o NAME,TYPE | awk '$2=="raid" {print "/dev/"$1}' || true)
if [ -n "$RAID_DEVICES" ]; then
    log_warn "发现以下RAID设备，将被停止并删除:"
    for raid in $RAID_DEVICES; do
        if [ -b "$raid" ]; then
            size=$(lsblk -dn -o SIZE "$raid" 2>/dev/null || echo "未知")
            echo "  - $raid (大小: $size)"
            mdadm --detail "$raid" 2>/dev/null | grep -E "Number|Device" || true
        fi
    done
    echo ""
fi

AVAILABLE_DISKS=()
while IFS= read -r disk; do
    # 检查是否为系统盘
    is_system_disk=0
    for sys_disk in "${SYSTEM_DISKS[@]}"; do
        if [ "$disk" == "$sys_disk" ]; then
            is_system_disk=1
            break
        fi
    done
    
    # 如果不是系统盘，添加到可用列表
    if [ $is_system_disk -eq 0 ]; then
        AVAILABLE_DISKS+=("$disk")
    fi
done < <(lsblk -dn -o NAME,TYPE | awk '$2=="disk" {print "/dev/"$1}')

# 检查是否有可用硬盘
if [ ${#AVAILABLE_DISKS[@]} -eq 0 ]; then
    log_warn "未发现除系统盘外的其他硬盘"
    exit 0
fi

# 显示将要操作的硬盘
echo ""
log_warn "================================================"
log_warn "警告: 以下硬盘将被完全清空并添加到LVM!"
log_warn "================================================"
echo ""
log_info "系统盘 (将被保留):"
for disk in "${SYSTEM_DISKS[@]}"; do
    size=$(lsblk -dn -o SIZE "$disk" 2>/dev/null || echo "未知")
    echo "  - $disk (大小: $size)"
done
echo ""
log_warn "将被清空的硬盘:"
for disk in "${AVAILABLE_DISKS[@]}"; do
    size=$(lsblk -dn -o SIZE "$disk")
    echo "  - $disk (大小: $size)"
done
echo ""

# 确认操作
read -p "$(echo -e ${RED}是否继续? 所有数据将被删除! [yes/NO]: ${NC})" confirm
if [ "$confirm" != "yes" ]; then
    log_info "操作已取消"
    exit 0
fi

# LVM配置参数
VG_NAME="vg_data"
LV_NAME="lv_data"
MOUNT_POINT="/data"

echo ""
log_info "开始配置LVM..."

# 首先停止所有RAID设备
if [ -n "$RAID_DEVICES" ]; then
    log_info "停止所有RAID设备..."
    for raid in $RAID_DEVICES; do
        if [ -b "$raid" ]; then
            log_info "  停止: $raid"
            # 卸载RAID设备
            umount -fl "$raid" 2>/dev/null || true
            # 停止RAID
            mdadm --stop "$raid" 2>/dev/null || true
            sleep 1
        fi
    done
    # 删除所有RAID配置
    mdadm --remove /dev/md* 2>/dev/null || true
    sleep 2
    log_info "RAID设备已停止"
    echo ""
fi

# 清空硬盘并创建物理卷
PV_LIST=()
for disk in "${AVAILABLE_DISKS[@]}"; do
    log_info "处理硬盘: $disk"
    
    # 1. 检查并删除设备映射器 (device mapper) - 仅针对当前硬盘
    log_info "  检查设备映射器..."
    for dm in /dev/mapper/*; do
        # 跳过系统设备映射器
        if [[ "$dm" == *"debian"* ]] || [[ "$dm" == *"root"* ]] || [[ "$dm" == *"swap"* ]]; then
            continue
        fi
        if [ -L "$dm" ]; then
            target=$(readlink -f "$dm")
            if [[ "$target" == ${disk}* ]]; then
                log_info "    删除映射: $dm -> $target"
                dmsetup remove -f "$dm" 2>/dev/null || true
            fi
        fi
    done
    sleep 1
    
    # 2. 停止所有使用该硬盘的进程
    log_info "  检查并停止使用该硬盘的进程..."
    fuser -km "$disk" 2>/dev/null || true
    fuser -km ${disk}* 2>/dev/null || true
    sleep 1
    
    # 3. 卸载所有分区
    log_info "  卸载所有分区..."
    for mount_point in $(lsblk -ln -o MOUNTPOINT "$disk" 2>/dev/null | grep -v '^$'); do
        log_info "    卸载: $mount_point"
        umount -fl "$mount_point" 2>/dev/null || true
    done
    
    # 使用设备名卸载
    for partition in $(lsblk -ln -o NAME "$disk" 2>/dev/null | tail -n +2); do
        umount -fl "/dev/$partition" 2>/dev/null || true
    done
    sleep 1
    
    # 4. 停用swap分区
    log_info "  停用swap分区..."
    swapoff ${disk}* 2>/dev/null || true
    sleep 1
    
    # 5. 删除LVM配置
    log_info "  删除LVM配置..."
    # 停用逻辑卷
    for lv in $(lvs --noheadings -o lv_path 2>/dev/null | grep "$disk" || true); do
        lvchange -an "$lv" 2>/dev/null || true
        lvremove -f "$lv" 2>/dev/null || true
    done
    
    # 停用卷组 (排除系统卷组)
    for vg in $(pvs --noheadings -o vg_name,pv_name 2>/dev/null | grep "${disk}" | awk '{print $1}' | grep -v debian-vg || true); do
        vgchange -an "$vg" 2>/dev/null || true
        vgremove -f "$vg" 2>/dev/null || true
    done
    
    # 删除物理卷
    for pv_part in ${disk}*; do
        if [ -b "$pv_part" ] && [ "$pv_part" != "$disk" ]; then
            pvremove -ff -y "$pv_part" 2>/dev/null || true
        fi
    done
    pvremove -ff -y "$disk" 2>/dev/null || true
    sleep 1
    
    # 6. 停用RAID
    log_info "  检查并停用RAID..."
    # 检查是否是RAID成员
    for md_dev in /dev/md*; do
        if [ -b "$md_dev" ]; then
            mdadm --detail "$md_dev" 2>/dev/null | grep -q "$disk" && {
                log_info "    停止RAID设备: $md_dev"
                mdadm --stop "$md_dev" 2>/dev/null || true
            }
        fi
    done
    
    # 清除RAID超级块
    mdadm --zero-superblock --force ${disk}* 2>/dev/null || true
    mdadm --zero-superblock --force "$disk" 2>/dev/null || true
    sleep 1
    
    # 7. 删除所有分区
    log_info "  删除所有分区..."
    # 使用sgdisk清除(更彻底)
    sgdisk --zap-all "$disk" 2>/dev/null || true
    # 或使用parted
    parted -s "$disk" mklabel gpt 2>/dev/null || true
    sleep 2
    
    # 9. 清除文件系统签名和分区表
    log_info "  清除文件系统签名..."
    wipefs --all --force "$disk" 2>/dev/null || true
    wipefs --all --force ${disk}* 2>/dev/null || true
    # 只清除前10MB，避免过度操作
    dd if=/dev/zero of="$disk" bs=1M count=10 conv=notrunc 2>/dev/null || true
    sleep 1
    
    # 10. 通知内核重新读取分区表
    blockdev --rereadpt "$disk" 2>/dev/null || true
    partprobe "$disk" 2>/dev/null || true
    sleep 3
    
    # 11. 创建新的GPT分区表
    log_info "  创建新的GPT分区表..."
    parted -s "$disk" mklabel gpt
    sleep 2
    
    # 12. 创建单个分区占用整个磁盘
    log_info "  创建分区..."
    parted -s "$disk" mkpart primary 0% 100%
    parted -s "$disk" set 1 lvm on
    sleep 3
    
    # 13. 再次通知内核
    partprobe "$disk"
    blockdev --rereadpt "$disk" 2>/dev/null || true
    sleep 3
    
    # 14. 确定分区设备名
    case "$disk" in
        *nvme*|*mmcblk*)
            PARTITION="${disk}p1"
            ;;
        *)
            PARTITION="${disk}1"
            ;;
    esac
    
    # 15. 等待分区设备出现并完全释放
    log_info "  等待分区设备: $PARTITION"
    for i in {1..15}; do
        if [ -b "$PARTITION" ]; then
            log_info "    分区设备已出现，等待完全就绪..."
            sleep 2
            # 确保没有进程使用
            fuser -km "$PARTITION" 2>/dev/null || true
            # 清除分区签名 (减少清零范围)
            wipefs --all --force "$PARTITION" 2>/dev/null || true
            dd if=/dev/zero of="$PARTITION" bs=1M count=1 conv=notrunc 2>/dev/null || true
            sleep 1
            break
        fi
        log_info "    等待中... ($i/15)"
        sleep 1
        partprobe "$disk" 2>/dev/null || true
    done
    
    if [ ! -b "$PARTITION" ]; then
        log_error "分区设备未出现: $PARTITION"
        exit 1
    fi
    
    # 16. 创建物理卷
    log_info "  创建物理卷: $PARTITION"
    sleep 2
    pvcreate -ff -y "$PARTITION"
    
    PV_LIST+=("$PARTITION")
    log_info "  完成: $disk"
    echo ""
done

# 创建卷组
log_info "创建卷组: $VG_NAME"
vgcreate "$VG_NAME" "${PV_LIST[@]}"

# 创建逻辑卷(使用所有可用空间)
log_info "创建逻辑卷: $LV_NAME (使用100%可用空间)"
lvcreate -l 100%FREE -n "$LV_NAME" "$VG_NAME"

# 格式化为ext4文件系统
log_info "格式化逻辑卷为ext4..."
mkfs.ext4 -F "/dev/$VG_NAME/$LV_NAME"

# 创建挂载点
if [ ! -d "$MOUNT_POINT" ]; then
    log_info "创建挂载点: $MOUNT_POINT"
    mkdir -p "$MOUNT_POINT"
fi

# 挂载逻辑卷
log_info "挂载逻辑卷到 $MOUNT_POINT"
mount "/dev/$VG_NAME/$LV_NAME" "$MOUNT_POINT"

# 添加到/etc/fstab实现开机自动挂载
log_info "配置开机自动挂载..."
FSTAB_ENTRY="/dev/$VG_NAME/$LV_NAME $MOUNT_POINT ext4 defaults 0 2"
if ! grep -q "$VG_NAME/$LV_NAME" /etc/fstab; then
    echo "$FSTAB_ENTRY" >> /etc/fstab
    log_info "已添加到 /etc/fstab"
else
    log_warn "/etc/fstab 中已存在该条目"
fi

# 显示配置结果
echo ""
log_info "================================================"
log_info "LVM配置完成!"
log_info "================================================"
echo ""
log_info "物理卷:"
pvdisplay | grep -E "PV Name|PV Size"
echo ""
log_info "卷组信息:"
vgdisplay "$VG_NAME" | grep -E "VG Name|VG Size|Free"
echo ""
log_info "逻辑卷信息:"
lvdisplay "/dev/$VG_NAME/$LV_NAME" | grep -E "LV Path|LV Size"
echo ""
log_info "挂载信息:"
df -h "$MOUNT_POINT"
echo ""
log_info "LVM已成功配置并挂载到: $MOUNT_POINT"

exit 0
