#!/bin/zsh
#
# Video Transcoding Script
# Converts video files to H.265/HEVC at 1080p for significant size reduction
#
# Usage: ./transcode.sh <folder> [options]
#
# Options:
#   --replace    Delete original files after successful transcoding
#   --dry-run    Show what would be transcoded without doing it
#   --crf <N>    Quality level (default: 20, lower=better quality, 18-28 typical)
#

set -e

# Configuration
DEFAULT_CRF=18
EXTENSIONS=("mov" "mp4" "m4v" "avi" "mkv" "mxf")

usage() {
    cat << EOF
Video Transcoding Script

Converts video files to H.265/HEVC at 1080p for significant size reduction.
Typically achieves 80-95% size reduction from uncompressed 4K footage.

Usage: $(basename "$0") <folder|file> [options]

Options:
  --replace     Delete original files after successful transcoding
  --dry-run     Show what would be transcoded without doing it
  --crf <N>     Quality level (default: 23)
                Lower = better quality, larger files
                18 = visually lossless, 23 = default, 28 = smaller
  --help        Show this help message

Output:
  Files are saved alongside originals with '_compressed.mp4' suffix.
  With --replace, originals are deleted after successful transcode.
  When processing a folder, files are sorted smallest-first.

Examples:
  $(basename "$0") ./video.mov                       # Transcode single file
  $(basename "$0") ./media/videos                    # Transcode folder (smallest first)
  $(basename "$0") ./media/videos --dry-run          # Preview only
  $(basename "$0") ./media/videos --replace          # Transcode and delete originals
  $(basename "$0") ./media/videos --crf 18           # Higher quality

EOF
    exit 0
}

# Parse arguments
FOLDER=""
REPLACE=false
DRY_RUN=false
CRF=$DEFAULT_CRF

while [[ $# -gt 0 ]]; do
    case "$1" in
        --replace)
            REPLACE=true
            shift
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        --crf)
            CRF="$2"
            shift 2
            ;;
        --help|-h)
            usage
            ;;
        -*)
            echo "Unknown option: $1"
            usage
            ;;
        *)
            if [[ -z "$FOLDER" ]]; then
                FOLDER="$1"
            fi
            shift
            ;;
    esac
done

# Validate arguments
if [[ -z "$FOLDER" ]]; then
    echo "Error: Folder path is required"
    echo ""
    usage
fi

if [[ ! -d "$FOLDER" && ! -f "$FOLDER" ]]; then
    echo "Error: Path not found: $FOLDER"
    exit 1
fi

# Check for ffmpeg
if ! command -v ffmpeg &> /dev/null; then
    echo "Error: ffmpeg is not installed"
    echo "Install with: brew install ffmpeg"
    exit 1
fi

# Build find pattern for video files
build_find_pattern() {
    local pattern=""
    for ext in "${EXTENSIONS[@]}"; do
        if [[ -n "$pattern" ]]; then
            pattern="$pattern -o"
        fi
        pattern="$pattern -iname '*.$ext'"
    done
    echo "$pattern"
}

# Format file size for display
format_size() {
    local bytes=$1
    if (( bytes >= 1073741824 )); then
        printf "%.1fG" $(echo "scale=1; $bytes / 1073741824" | bc)
    elif (( bytes >= 1048576 )); then
        printf "%.1fM" $(echo "scale=1; $bytes / 1048576" | bc)
    else
        printf "%.1fK" $(echo "scale=1; $bytes / 1024" | bc)
    fi
}

# Get file size in bytes
get_size() {
    stat -f%z "$1" 2>/dev/null || echo 0
}

echo "========================================"
echo "Video Transcoder"
echo "========================================"
echo "Folder:      $FOLDER"
echo "Codec:       H.265/HEVC"
echo "Resolution:  1080p (scaled down)"
echo "Quality:     CRF $CRF"
echo "Replace:     $REPLACE"
[[ "$DRY_RUN" == true ]] && echo "DRY RUN:     Yes"
echo "========================================"
echo ""

# Check if input is a file or folder
IS_SINGLE_FILE=false
if [[ -f "$FOLDER" ]]; then
    IS_SINGLE_FILE=true
    VIDEO_FILES=("$FOLDER")
    echo "Processing single file"
    echo ""
else
    # Find all video files (excluding already compressed ones), sorted by size (smallest first)
    FIND_PATTERN=$(build_find_pattern)

    # Use Python for robust sorting by size (handles spaces in paths correctly)
    VIDEO_FILES=()
    while IFS= read -r file; do
        VIDEO_FILES+=("$file")
    done < <(eval "find \"$FOLDER\" -type f \( $FIND_PATTERN \)" 2>/dev/null | python3 -c "
import sys
import os

files = []
for line in sys.stdin:
    path = line.rstrip('\n')
    if '_compressed.' in path:
        continue
    try:
        size = os.path.getsize(path)
        files.append((size, path))
    except:
        pass

files.sort(key=lambda x: x[0])
for size, path in files:
    print(path)
")

    if [[ ${#VIDEO_FILES[@]} -eq 0 ]]; then
        echo "No video files found in $FOLDER"
        exit 0
    fi

    echo "Found ${#VIDEO_FILES[@]} video file(s) to process (smallest first)"
    echo ""
fi

# Track totals
TOTAL_ORIGINAL=0
TOTAL_COMPRESSED=0
SUCCESS_COUNT=0
FAIL_COUNT=0

# Process each file
for input_file in "${VIDEO_FILES[@]}"; do
    # Generate output filename
    dir=$(dirname "$input_file")
    basename=$(basename "$input_file")
    name="${basename%.*}"
    output_file="$dir/${name}_compressed.mp4"

    # Get original size
    original_size=$(get_size "$input_file")
    original_size_fmt=$(format_size $original_size)

    echo "----------------------------------------"
    echo "Input:  $input_file ($original_size_fmt)"
    echo "Output: $output_file"

    if [[ "$DRY_RUN" == true ]]; then
        echo "Action: [DRY RUN] Would transcode"
        continue
    fi

    # Skip if output already exists
    if [[ -f "$output_file" ]]; then
        echo "Action: Skipped (output already exists)"
        continue
    fi

    # Transcode with ffmpeg
    # -vf scale=-2:1080  = scale to 1080p height, auto width (divisible by 2)
    # -c:v libx265       = H.265/HEVC codec
    # -crf               = quality (lower = better)
    # -preset medium     = encoding speed/compression tradeoff
    # -c:a aac -b:a 128k = audio to AAC at 128kbps
    # -tag:v hvc1        = compatibility tag for Apple devices
    echo "Action: Transcoding..."

    if ffmpeg -i "$input_file" \
        -vf "scale=-2:1080" \
        -c:v libx265 \
        -crf "$CRF" \
        -preset medium \
        -c:a aac -b:a 128k \
        -tag:v hvc1 \
        -y \
        "$output_file" \
        -loglevel warning -stats 2>&1; then

        # Get compressed size
        compressed_size=$(get_size "$output_file")
        compressed_size_fmt=$(format_size $compressed_size)

        # Calculate reduction
        if (( original_size > 0 )); then
            reduction=$(echo "scale=1; 100 - ($compressed_size * 100 / $original_size)" | bc)
        else
            reduction=0
        fi

        echo "Result: $original_size_fmt → $compressed_size_fmt (${reduction}% reduction)"

        TOTAL_ORIGINAL=$((TOTAL_ORIGINAL + original_size))
        TOTAL_COMPRESSED=$((TOTAL_COMPRESSED + compressed_size))
        ((SUCCESS_COUNT++))

        # Delete original if --replace is set
        if [[ "$REPLACE" == true ]]; then
            rm "$input_file"
            echo "Deleted: $input_file"
        fi
    else
        echo "Result: FAILED"
        ((FAIL_COUNT++))
        # Remove partial output file if it exists
        [[ -f "$output_file" ]] && rm "$output_file"
    fi
done

echo ""
echo "========================================"
echo "Summary"
echo "========================================"
if [[ "$DRY_RUN" == true ]]; then
    echo "DRY RUN - no files were transcoded"
else
    echo "Processed: $SUCCESS_COUNT succeeded, $FAIL_COUNT failed"
    if (( SUCCESS_COUNT > 0 )); then
        total_orig_fmt=$(format_size $TOTAL_ORIGINAL)
        total_comp_fmt=$(format_size $TOTAL_COMPRESSED)
        if (( TOTAL_ORIGINAL > 0 )); then
            total_reduction=$(echo "scale=1; 100 - ($TOTAL_COMPRESSED * 100 / $TOTAL_ORIGINAL)" | bc)
        else
            total_reduction=0
        fi
        echo "Total:     $total_orig_fmt → $total_comp_fmt (${total_reduction}% reduction)"

        if [[ "$REPLACE" == true ]]; then
            saved=$(format_size $((TOTAL_ORIGINAL - TOTAL_COMPRESSED)))
            echo "Freed:     $saved"
        fi
    fi
fi
echo "========================================"
