#!/bin/bash

# This file is part of RobinHood
# Copyright (C) 2025 Commissariat a l'energie atomique et aux energies
#                    alternatives
#
# SPDX-License-Identifier: LGPL-3.0-or-later

function is_numeric
{
    local date="$1"

    re='^[0-9]+$'
    if ! [[ $date =~ $re ]] ; then
        return 1
    fi

    return 0
}

function get_retention_attribute
{
    local config_file="$RBH_CONFIG_PATH"

    if [ -z "$config_file" ]; then
        config_file="/etc/robinhood4.d/default.yaml"
    fi

    if [ ! -f "$config_file" ]; then
        echo "user.expires"
        return 0
    fi

    local retention_attr="$(cat $config_file | grep "RBH_RETENTION_XATTR" |
                                               cut -d':' -f2 | xargs)"
    echo "${retention_attr//\"}"
}

function rbh_set_retention
{
    local entry="$1"
    local retention="$2"
    local prefix
    local date
    local rc

    local retention_attr="$(get_retention_attribute)"

    if is_numeric "$retention"; then
        date="$(date --date="@$retention" +%s)"
        rc=$?
    elif [ "$retention" == "inf" ]; then
        date="inf"
        rc=0
    else
        date="$(date --date="$retention" +%s)"
        rc=$?
    fi

    if (( rc != 0 )); then
        echo "$date"
        exit $rc
    fi

    if [ ! -z "$relative_found" ]; then
        prefix="+"
        local current_date="$(date +%s)"
        date="$(( date - current_date ))"
    fi

    setfattr -h -n "$retention_attr" -v "$prefix$date" "$entry"
}

function get_complete_timedate
{
    local retention="$1"
    local timer=()

    local seconds=$((retention % 60))
    ((retention /= 60))
    local minutes=$((retention % 60))
    ((retention /= 60))
    local hours=$((retention % 24))
    ((retention /= 24))
    local days=$((retention))

    if (( days != 0 )); then
        timer+=("$(printf "%d day(s)" $days)")
    fi

    if (( hours != 0 )); then
        timer+=("$(printf "%d hour(s)" $hours)")
    fi

    if (( minutes != 0 )); then
        timer+=("$(printf "%d minute(s)" $minutes)")
    fi

    if (( seconds != 0 )); then
        timer+=("$(printf "%d second(s)" $seconds)")
    fi

    local timestamp
    if (( ${#timer[@]} == 1 )); then
        timestamp="${timer[0]}"
    elif (( ${#timer[@]} == 2 )); then
        timestamp="${timer[0]} and ${timer[1]}"
    else
        # some bashism to get all array elements but the last one, surround
        # each with ", ", remove the starting two, and add a " and " between
        # the second-to-last and last elements
        timestamp=$(printf ", %s" "${timer[@]::${#timer[@]}-1}")
        timestamp=${timestamp:2}
        timestamp="$timestamp and ${timer[-1]}"
    fi

    echo "$timestamp"
}

function rbh_get_retention
{
    local entry="$1"

    local retention_attr="$(get_retention_attribute)"

    local retention="$(getfattr -h -n "$retention_attr" --only-values --absolute-names $entry 2>/dev/null)"

    if [ -z "$retention" ]; then
        echo "'$entry' has no retention date set"
        exit $rc
    fi

    if [ "$retention" == "inf" ]; then
        echo "'$entry' has an infinite retention date set"
        exit 0
    fi

    local relative="${retention:0:1}"
    if [ "$relative" == "+" ]; then
        retention="${retention:1}"

        if ! is_numeric "$retention"; then
            echo "Retention value set on '$entry' is not a number"
            exit 1
        fi

        local timedate="$(get_complete_timedate "$retention")"
        local entry_mtime="$(stat -c "%Y" "$entry")"
        local expiration="$(( entry_mtime + retention ))"

        echo "'$entry' is set to expire $timedate after its last" \
             "modification time, so '$entry' expires on '$(date --date="@$expiration")'"
    else
        local date="$(date --date="@$retention")"
        local rc=$?

        if (( rc != 0 )); then
            echo "Recorded retention date is not a valid date: '$date'"
            exit $rc
        fi

        echo "'$entry' is set to expire on '$date'"
    fi
}

function rbh_rm_retention
{
    local entry="$1"

    local retention_attr="$(get_retention_attribute)"
    setfattr --remove="$retention_attr" "$entry"
}

function print_doc
{
    echo "Usage: rbh-retention entry [retention_date [--relative]] [--remove] [--help]"
    echo ""
    echo "Interact with an entry's retention date."
    echo "If only an entry's path is provided, show its retention date."
    echo "If both an entry and a retention date are given, set the entry's"
    echo "retention date."
    echo ""
    echo "The retention date should be given either as a 'date(1)'-compatible"
    echo "format, a timestamp, or the word 'inf' (in which case the entry is"
    echo "set to have an infinite retention date). If the '--relative' flag is"
    echo "given, the entry's expiration date will be relative to its"
    echo "modification time."
    echo ""
    echo "'--remove'        remove the entry's retention date"
    echo "'--relative'      whether the given retention date should be relative"
    echo "                  to the entry's modification time"
    echo "'--help'          print this message"
    echo ""
    echo "See 'date(1)''s man page for additional information about compatible formats."
    echo ""
}

if [ $# -lt 1 ]; then
    echo "Please specify the entry from/on which to get/set the retention date"
    echo ""
    print_doc
    exit 1
fi

while :
do
    if [ -z "$1" ]; then
        break
    fi

    case "$1" in
        --remove)
            remove_found=true
            ;;
        --relative)
            relative_found=true
            ;;
        --help)
            print_doc
            exit 0
            ;;
        *)
            if [ -z "$entry" ]; then
                entry="$1"
                shift
                continue
            fi

            if [ -z "$retention_date" ]; then
                retention_date="$1"
                shift
                continue
            fi

            echo "Entry and retention date already given"
            exit 1
            ;;
    esac

    shift
done

if [ -z "$entry" ]; then
    echo "Please specify the entry from/on which to get/set the retention date"
    echo ""
    print_doc
    exit 1
fi

full_path="$(realpath "$entry")"
check_exists="$(stat "$full_path")"
rc=$?

if (( rc != 0 )); then
    echo "'$full_path' does not exist"
    echo ""
    print_doc
    exit 1
fi

if [ ! -z "$remove_found" ]; then
    rbh_rm_retention "$full_path"
    exit 0
fi

if [ -z "$retention_date" ]; then
    rbh_get_retention "$full_path"
else
    rbh_set_retention "$full_path" "$retention_date"
fi
