#! /bin/bash
# vim: shiftwidth=4 tabstop=4 expandtab

subcommand__help() {
    if [[ -z "$2" ]] || [[ "$2" == "ls" ]]; then
        echo "$COMMAND ls [-J] [hyp(s)]"
        if [[ "$2" == "ls" ]]; then
            echo "  List CD-ROM ISO files present on hypervisors"
            echo "    -J|--json          JSON output"
            echo "    hyp(s)             Optional list of hypervisors to handle"
        fi
    fi
}

get_isos_from_hyp() {
    local hyp="$1"
    local calc_md5="$2"

    local remote_cmd='
        dir="/var/lib/libvirt/cdroms"
        calc_md5="'"$calc_md5"'"
        hyp="'"$hyp"'"

        [ -d "$dir" ] || exit 0

        for iso in "$dir"/*.iso; do
            [ -f "$iso" ] || continue

            name=$(basename "$iso")
            size=$(du -k "$iso" | cut -f1)

            if [ "$calc_md5" -eq 1 ]; then
                md5=$(md5sum "$iso" | cut -d" " -f1)
                jo hyp="$hyp" name="$name" size="$size" md5="$md5"
            else
                jo hyp="$hyp" name="$name" size="$size"
            fi
        done
    '

    run_on_hyp "$hyp" bash -c "$remote_cmd" 2>/dev/null
}

get_hyps_isos() {
    local md5=0
    local target_hyps=()

    # Parse parameters
    for arg in "$@"; do
        if [[ "$arg" == "--md5" ]]; then
            md5=1
        else
            target_hyps+=("$arg")
        fi
    done

    # Fallback to $HYPS if no hypervisors passed in arguments
    if [[ ${#target_hyps[@]} -eq 0 ]]; then
        # Use read to split $HYPS into array using spaces/tabs
        read -r -a target_hyps <<< "$HYPS"
    fi

    for hyp in "${target_hyps[@]}"; do
        get_isos_from_hyp "$hyp" "$md5"
    done | jq -s '
        group_by(.name) | map(
            . as $group |

            # Find the most frequent {size, md5} signature (mode)
            # In case of a tie, group_by preserves the original order of appearance
            (
                $group
                | group_by({size: .size, md5: .md5})
                | sort_by(-length)[0][0]
            ) as $majority |

            ($majority.size) as $ref_size |
            ($majority.md5) as $ref_md5 |

            {
                name: $group[0].name,
                size: $ref_size,
                hyps: [ $group[] | select(.size == $ref_size and (.md5 == $ref_md5 or .md5 == null)) | .hyp ],
                mismatch_hyps: [ $group[] | select(.size != $ref_size or (.md5 != null and .md5 != $ref_md5)) | .hyp ]
            } + (
                if $ref_md5 then {md5: $ref_md5} else {} end
            )
        )'
}

subcommand__ls() {
    check_generate_script_not_supported
    local isos json=0 md5=0 opt
    local -a hyps args
    for opt in "${@:1}"; do
        case $opt in
            -J|--json)
                json=1
                ;;
            --md5)
                md5=1
                ;;
            *)
                if check_hyp "$opt"; then
                    hyps+=( "$opt" )
                else
                    usage "Unknown parameter '$opt'"
                fi
                ;;
        esac
    done

    [[ ${#hyps[@]} -eq 0 ]] && read -r -a hyps <<< "$HYPS"

    if [[ "$md5" -eq 1 ]]; then
        isos=$( get_hyps_isos --md5 "${hyps[@]}" )
    else
        isos=$( get_hyps_isos "${hyps[@]}" )
    fi

    if [[ $json -eq 1 ]]; then
        jq <<< "$isos"
        exit 0
    fi

    args=( -n Name Size )
    [[ $md5 -eq 1 ]] && args+=( MD5 )
    for hyp in "${hyps[@]}"; do
        args+=( "$hyp" -a "$hyp" c )
    done

    jq --argjson hyps "$( jo -a "${hyps[@]}" )" "$JQ_FORMAT_SIZE"'
        map(
            . as $iso |

            (
                $hyps | map(
                    . as $h |
                    {
                    key: $h,
                    value: (
                        if ($iso.mismatch_hyps // [] | index($h)) then "!"
                        elif ($iso.hyps // [] | index($h)) then "X"
                        else ""
                        end
                    )
                    }
                ) | from_entries
            ) as $matrix |

            {
                Name: .name,
                Size: .size | format_size
            }
            + (if .md5 then {MD5: .md5} else {} end)
            + $matrix
        )
    ' <<< "$isos"  | print_json_table "${args[@]}"
}

run_subcommand "${COMMAND_ARGS[@]}"
