#!/bin/sh

# Exit immediately if a command exits with a non-zero status
set -e

FIRST_PHYSICAL_EXTENT_OFFSET_BYTES=1048576  # 1MB
PARTITION_HEADER_SIZE=1048576               # 1MB
REGULAR_LVM_PHYSICAL_EXTENT_SIZE=1048576    # 1MB
RESERVED_SIZE_MB=0

print_usage() {
    echo "Usage: $0 --device=<device> --vg-name=<name> --lv-name=<name> [--reserve=<mb>]"
    echo "  --device=<device>    Path to the device to migrate"
    echo "  --vg-name=<name>     Volume group name (required)"
    echo "  --lv-name=<name>     Logical volume name (required)"
    echo "  --reserve=<mb>       Reserve space in MB for additional logical volumes (optional)"
    echo "  --help              Show this help message"
}

generate_id() {
    hexdump -n 16 -e '4/4 "%08X"' /dev/urandom | tr -d '\n'
}

get_device_size() {
    device="$1"
    blockdev --getsize64 "$device" 2>/dev/null || {
        echo "Failed to get device size for $device" >&2
        exit 1
    }
}

resize_filesystem() {
    device="$1"
    target_size_bytes="$2"
    # Round to 1MB due to LVM extent size
    target_size_mb=$(awk "BEGIN {print int($target_size_bytes / 1048576)}")
    resize2fs "$device" "${target_size_mb}M" || {
        echo "Error: Failed to resize filesystem on $device to ${target_size_mb}MB" >&2
        exit 1
    }
}

copy_header() {
    device="$1"
    src_offset="$2"
    dst_offset="$3"

    dd if="$device" of="$device" bs=512 count=2048 \
       seek=$(awk "BEGIN {print int($dst_offset / 512)}") skip=$(awk "BEGIN {print int($src_offset / 512)}") \
       conv=notrunc
}

create_lvm_metadata() {
    device="$1"
    device_size="$2"
    vg_name="$3"
    lv_name="$4"
    reserved_size_mb="$5"

    # Calculate extents
    total_extents=$(awk "BEGIN {print int(($device_size - $FIRST_PHYSICAL_EXTENT_OFFSET_BYTES) / $REGULAR_LVM_PHYSICAL_EXTENT_SIZE)}")
    header_extents=1  # 1MB header
    reserved_extents="$reserved_size_mb"  # Reserved space in MB = extents (since 1 extent = 1MB)
    main_data_extents=$(awk "BEGIN {print $total_extents - $header_extents - $reserved_extents}")

    # Generate random IDs
    vg_id=$(generate_id)
    pv_id=$(generate_id)
    lv_id=$(generate_id)

    creation_time=$(date +%s)

    # Generate metadata
    cat > /tmp/vgcfgrestore.txt << EOF
contents = "Text Format Volume Group"
version = 1
description = "Generated by lvm_migrator"
creation_host = "localhost"
creation_time = $creation_time
$vg_name {
  id = "$vg_id"
  seqno = 0
  format = "lvm2"
  status = ["READ", "WRITE", "RESIZEABLE"]
  flags = []
  extent_size = 2048
  max_lv = 0
  max_pv = 1
  metadata_copies = 0
  physical_volumes {

  pv0 {
    id = "$pv_id"
    device = "$device"
    status = ["ALLOCATABLE"]
    flags = []
    dev_size = $(awk "BEGIN {print $total_extents * 2048}")
    pe_start = 2048
    pe_count = $total_extents
  }
  }
  logical_volumes {

    $lv_name {
    id = "$lv_id"
    status = ["READ", "WRITE", "VISIBLE"]
    flags = []
    creation_time = $creation_time
    creation_host = "localhost"
    segment_count = 2

      segment1 {
        start_extent = 0
        extent_count = 1
        type = "striped"
stripe_count = 1
stripes = [
"pv0", $(awk "BEGIN {print $total_extents - 1}")
]

      }
      segment2 {
        start_extent = 1
        extent_count = $main_data_extents
        type = "striped"
stripe_count = 1
stripes = [
"pv0", 0
]

      }
    }
  }
}
EOF

    # Create physical volume
    pvcreate --uuid "$pv_id" --restorefile /tmp/vgcfgrestore.txt "$device"

    # Restore volume group
    vgcfgrestore -f /tmp/vgcfgrestore.txt "$vg_name"

    # Enable the logical volume group
    vgchange -ay "$vg_name"

    # Remove temporary metadata file
    rm -f /tmp/vgcfgrestore.txt

    echo "Created volume group: $vg_name"
    echo "Created logical volume: $vg_name-$lv_name"
}

# Parse command line arguments
while [ $# -gt 0 ]; do
    case "$1" in
        --help)
            print_usage
            exit 0
            ;;
        --device=*)
            DEVICE_PATH="${1#*=}"
            shift
            ;;
        --vg-name=*)
            VG_NAME="${1#*=}"
            shift
            ;;
        --lv-name=*)
            LV_NAME="${1#*=}"
            shift
            ;;
        --reserve=*)
            RESERVED_SIZE_MB="${1#*=}"
            shift
            ;;
        *)
            echo "Unknown argument: $1" >&2
            print_usage
            exit 1
            ;;
    esac
done

# Validate required arguments
if [ -z "${DEVICE_PATH:-}" ]; then
    echo "Error: --device argument is required" >&2
    print_usage
    exit 1
fi

if [ -z "${VG_NAME:-}" ]; then
    echo "Error: --vg-name argument is required" >&2
    print_usage
    exit 1
fi

if [ -z "${LV_NAME:-}" ]; then
    echo "Error: --lv-name argument is required" >&2
    print_usage
    exit 1
fi

# Validate reserve size
case "$RESERVED_SIZE_MB" in
    ''|*[!0-9]*)
        echo "Error: Invalid reserve size: $RESERVED_SIZE_MB" >&2
        exit 1
        ;;
esac

# Check if volume group already exists
if vgs "$VG_NAME" >/dev/null 2>&1; then
    echo "Error: Volume group '$VG_NAME' already exists" >&2
    echo "Cannot create a new volume group with the same name" >&2
    exit 1
fi

# Main migration logic
echo "Starting LVM migration for device: $DEVICE_PATH"

# Get device size
DEVICE_SIZE=$(get_device_size "$DEVICE_PATH")
DEVICE_SIZE_MB=$(awk "BEGIN {print int($DEVICE_SIZE / 1048576)}")
echo "Device size: $DEVICE_SIZE bytes ($DEVICE_SIZE_MB MB)"

# Calculate filesystem size (reserve 1MB for LVM header + optional reserved space)
RESERVED_SIZE_BYTES=$(awk "BEGIN {print $RESERVED_SIZE_MB * 1048576}")
# Round down to nearest MB
FILESYSTEM_SIZE_MB=$(awk "BEGIN {print int(($DEVICE_SIZE - $FIRST_PHYSICAL_EXTENT_OFFSET_BYTES - $RESERVED_SIZE_BYTES) / 1048576)}")
FILESYSTEM_SIZE=$(awk "BEGIN {print $FILESYSTEM_SIZE_MB * 1048576}")

echo "Resizing filesystem to: $FILESYSTEM_SIZE bytes ($FILESYSTEM_SIZE_MB MB)"
if [ "$RESERVED_SIZE_MB" -gt 0 ]; then
    echo "Reserving $RESERVED_SIZE_MB MB for additional logical volumes"
fi

# Resize filesystem
echo "Resizing filesystem..."
resize_filesystem "$DEVICE_PATH" "$FILESYSTEM_SIZE"

# Copy header to end
HEADER_OFFSET="$FILESYSTEM_SIZE"
echo "Copying 1MB header to offset: $HEADER_OFFSET"
copy_header "$DEVICE_PATH" 0 "$HEADER_OFFSET"

# Create LVM metadata
echo "Creating LVM metadata..."
create_lvm_metadata "$DEVICE_PATH" "$DEVICE_SIZE" "$VG_NAME" "$LV_NAME" "$RESERVED_SIZE_MB"

echo "Migration completed successfully!"
