#!/bin/bash

## Copyright (C) 2024, 2025 Ajrat Makhmutov <rauty@altlinux.org>
##
## This file is part of alterator-kopidel.
##
## alterator-kopidel is free software: you can redistribute it and/or modify it under the terms
## of the GNU General Public License as published by the Free Software Foundation,
## either version 3 of the License, or (at your option) any later version.
##
## alterator-kopidel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
## without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
## See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with alterator-kopidel.
## If not, see <https://www.gnu.org/licenses/>.

alterator_api_version=1
. alterator-sh-functions

. "$(rpm --eval "%_libexecdir")/alterator-kopidel/kopidel-steps"

ACTIONS=()
run_action() (
	action="$1"
	echo "action start: $action"

	"$action" || return 1

	echo "action end: $action"
)

print_required_space() (
	# We will never copy the working directory to avoid recursion.
	copied_fs_size=$(du -bs \
		--exclude-from="$IGNORED_FILES" \
		--exclude="$WORKDIR_NAME" \
		/ 2>/dev/null | cut -f 1)
	# At some moment, there are two copies of the file system (in directory and tar)
	# and squashfs with other necessary files in the working directory (1 GB).
	echo "$(( copied_fs_size * 2 + 1024 * 1024 * 1024 ))"
)

create_razlivochniy_obraz::check_parameters() {
	echo "Parameters:"
	echo "workdir - $in_workdir"
	echo "async - $in_async"
	echo "oem_mode - $in_oem_mode"
	echo "compress_copied - $in_compress_copied"
	echo "grub_device - $in_grub_device"
	echo "in_ignored_files - $in_ignored_files"

	if [[ -z "$in_workdir" || "$in_workdir" == "no_available" ]]; then
		write_error "The workdir variable is not specified."
		return 1
	fi
	if [[ -e "$in_workdir" && ! -d "$in_workdir" ]]; then
		write_error "The workdir variable is not a directory path."
		return 1
	fi
	if [[ $(basename "$in_workdir") != "$WORKDIR_NAME" ]]; then
		write_error "Basename of workdir is not $WORKDIR_NAME"
		return 1
	fi

	# It is necessary to check the availability of free space in workdir.
	mkdir -pv "$in_workdir"

	update_ignored_files || return 1
	workdir_size=$(du -bs "$in_workdir" | cut -f 1)
	required_space=$(print_required_space)
	avail=$(df --output=avail -B 1 "$in_workdir" | tail -n 1)
	if [[ -z "$avail" || "$((avail + workdir_size))" -lt "$required_space" ]]; then
		write_error "Not enough free space in workdir $in_workdir: available - $avail, required - $required_space, workdir size - $workdir_size"
		return 1
	fi
}

alterator-mailbox-send-wrapper() {
	if [ -S /tmp/.private/root/alterator/browser-sock ]; then
		TMPDIR=/tmp/.private/root/ alterator-mailbox-send "$1"
	else
		alterator-mailbox-send "$1"
	fi
}

step_percentage_mailbox_sender() (
	alterator-mailbox-send-wrapper "step_progress: 0"
	while read -r line; do
		alterator-mailbox-send-wrapper "$line"
	done
	alterator-mailbox-send-wrapper "step_progress: 100"
)

run_step_ui_wrapper() (
	run_step "$1" | step_percentage_mailbox_sender
	if [ "${PIPESTATUS[0]}" -ne 0 ]; then
		echo "Failed on the step $1"
		return 1
	fi
)

ACTIONS+=( create_razlivochniy_obraz )
create_razlivochniy_obraz() (
	alterator-mailbox-send-wrapper "step: check_parameters main_progress: 0"
	create_razlivochniy_obraz::check_parameters || return 1

	rm -rf "$in_workdir"
	mkdir -pv "$in_workdir/image/Metadata"
	cd "$in_workdir" || return 1

	alterator-mailbox-send-wrapper "step: prepare_workdir main_progress: 3"
	run_step_ui_wrapper prepare_workdir || return 1
	alterator-mailbox-send-wrapper "step: create_copied_fs main_progress: 40"
	run_step_ui_wrapper create_copied_fs || return 1
	alterator-mailbox-send-wrapper "step: create_disk_partition_info main_progress: 70"
	alterator-mailbox-send-wrapper "step_progress: -1" # Running sausage.
	run_step_ui_wrapper create_disk_partition_info || return 1
	alterator-mailbox-send-wrapper "step: create_autoinstall_scm main_progress: 75"
	alterator-mailbox-send-wrapper "step_progress: -1" # Running sausage.
	run_step_ui_wrapper create_autoinstall_scm || return 1
	alterator-mailbox-send-wrapper "step: create_system_iso main_progress: 80"
	run_step_ui_wrapper create_system_iso || return 1
	alterator-mailbox-send-wrapper "step: done main_progress: 100"
)

ignored_files_err_msg=$(_ "An unreadable custom list of ignored files is specified")
ACTIONS+=( workdirs_list )
workdirs_list() (
	if ! update_ignored_files; then
		write_enum_item "" "$ignored_files_err_msg"
		return 0
	fi

	required_space="$(print_required_space)"

	flag_no_available="true"

	while read -r line; do
		source=$( echo "$line" | cut -d ' ' -f 1)
		target=$( echo "$line" | cut -d ' ' -f 2)
		options=$(echo "$line" | cut -d ' ' -f 3)
		avail=$(  echo "$line" | cut -d ' ' -f 4)

		( for avoided_mount_flag in ro nodev; do
			if echo "$options" | grep -q avoided_mount_flag; then
				echo "$target in $source: The avoided mount flag $avoided_mount_flag has been detected!"
				return 1
			fi
		done ) || continue

		workdir_size=$(du -bs "$target/$WORKDIR_NAME" | cut -f 1)

		echo "$target in $source: available:$avail required_space:$required_space workdir_size:$workdir_size"
		if [[ -z "$avail" || "$((avail + workdir_size))" -lt "$required_space" ]]; then
			echo "$target in $source: not enough free space"
			continue
		fi

		if [ "$target" == '/' ]; then
			target=''
		fi
		write_enum_item "$target/$WORKDIR_NAME" "$target/$WORKDIR_NAME $(_ "in partition") $source"

		flag_no_available="false"
	done < <(findmnt -nbro 'source,target,options,avail' | grep -v ' 0$')

	if [ "$flag_no_available" == "true" ]; then
		required_space_human=$(numfmt --to=iec-i "$required_space")
		write_enum_item "no_available" "$(_ "There are no partitions available with enough free space. Required space:") $required_space_human."
	fi
)

on_message() {
	if [ -z "$in_action" ]; then
		return 0
	fi
	if [[ " ${ACTIONS[*]} " =~ " ${in_action} " ]]; then
		if test_bool "$in_async"; then
			run_action "$in_action" &
		else
			run_action "$in_action"
		fi
	elif [[ "$ALTERATOR_DEBUG" == 1 && " ${STEPS[*]} ${SUBSTEPS[*]} " =~ " ${in_action} " ]]; then
		pushd "$in_workdir" || return 1

		run_step "$in_action" || return 1

		popd || return 1
	else
		write_error "Unknown action: $in_action."
		return 1
	fi
}

message_loop on_message
