#!/bin/bash
# Torrent Mover v8.0 - Enhanced & Robust Version with modular architecture,
# improved error handling, security, and content categorization
#
# This script processes completed torrents reported by Transmission,
# moving or copying files to designated destination directories.
# It includes robust locking, advanced error handling & notifications,
# improved logging, optional post-transfer integrity checks, configurable path mapping,
# and improved archive extraction that preserves directory structure.

# Set script location for importing modules
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
LIB_DIR="/usr/local/lib/torrent-mover"

##############################
# Robust Locking with flock  #
##############################
LOCK_FILE="/var/lock/torrent-mover.lock"
exec 200>"${LOCK_FILE}" || { echo "Cannot open lock file" >&2; exit 1; }
flock -n 200 || { echo "Another instance is running." >&2; exit 1; }

##############################
# Configuration & Validation #
##############################
CONFIG_FILE="/etc/torrent/mover.conf"
if [[ ! -f "${CONFIG_FILE}" ]]; then
    echo "FATAL: Configuration file missing: ${CONFIG_FILE}" >&2
    exit 1
fi
source "${CONFIG_FILE}"

# Validate required configuration values.
if [[ -z "${TRANSMISSION_PATH_PREFIX:-}" || -z "${LOCAL_PATH_PREFIX:-}" ]]; then
    echo "FATAL: TRANSMISSION_PATH_PREFIX and LOCAL_PATH_PREFIX must be set in ${CONFIG_FILE}" >&2
    exit 1
fi

# Load modules
for module in "${LIB_DIR}"/*.sh; do
    if [[ -f "$module" ]]; then
        source "$module"
    fi
done

# Set defaults for new configuration options
TORRENT_USER="${TORRENT_USER:-debian-transmission}"
TORRENT_GROUP="${TORRENT_GROUP:-debian-transmission}"
MAX_RETRY_ATTEMPTS="${MAX_RETRY_ATTEMPTS:-3}"
RETRY_WAIT_TIME="${RETRY_WAIT_TIME:-15}"

# Enable DEBUG mode if set in config
if [[ "${LOG_LEVEL}" == "DEBUG" ]]; then
    DEBUG=1
fi

# Parse STORAGE_DIRS into an array.
STORAGE_DIRS_ARRAY=()
if [[ -n "${STORAGE_DIRS}" ]]; then
    IFS=',' read -ra STORAGE_DIRS_ARRAY <<< "${STORAGE_DIRS}"
fi

#################################
# Error Handling & Notifications#
#################################
trap 'error_handler ${LINENO} "$BASH_COMMAND"' ERR

#################
# Main Function #
#################
main() {
    check_dependencies

    # Validate destination directories.
    declare -a REQUIRED_DIRS=(
        "${DIR_GAMES_DST}"
        "${DIR_APPS_DST}"
        "${DIR_MOVIES_DST}"
        "${DIR_BOOKS_DST}"
        "${DEFAULT_DST}"
    )
    
    # Add optional directories if defined
    [[ -n "${DIR_TV_DST}" ]] && REQUIRED_DIRS+=("${DIR_TV_DST}")
    [[ -n "${DIR_MUSIC_DST}" ]] && REQUIRED_DIRS+=("${DIR_MUSIC_DST}")
    
    # Create required directories if they don't exist
    log_info "Creating required directories if they don't exist..."
    for dir in "${REQUIRED_DIRS[@]}"; do
        if [[ -n "$dir" ]]; then
            if [[ ! -d "$dir" ]]; then
                log_info "Creating directory: $dir"
                if mkdir -p "$dir"; then
                    # Try to set permissions but don't fail if it doesn't work
                    chmod 775 "$dir" 2>/dev/null || log_warn "Could not set permissions on $dir"
                    chown ${TORRENT_USER:-debian-transmission}:${TORRENT_GROUP:-debian-transmission} "$dir" 2>/dev/null || log_warn "Could not set ownership on $dir"
                    log_info "Created directory: $dir"
                else
                    log_error "Failed to create directory: $dir"
                fi
            fi
        fi
    done

    # Now validate that all required directories exist and are writable
    validate_directories "${REQUIRED_DIRS[@]}" || exit 1

    init_checksum_db

    if (( CACHE_WARMUP )); then
        warm_cache
        exit 0
    fi

    log_info "Starting processing with user: ${TORRENT_USER}"
    declare -A warned_dirs=()
    
    # Get list of torrents from Transmission
    log_debug "Getting list of torrents..."
    local torrent_ids
    torrent_ids=$(get_torrents)
    log_info "Found $(echo "$torrent_ids" | wc -l) torrents"
    
    # Use a regular for loop instead of a pipe to while
    # to avoid the subshell issue that causes processed_source_dirs to be lost
    readarray -t torrent_ids_array <<< "$torrent_ids"
    
    # Print the torrent IDs to debug (always, not just in debug mode)
    if [[ ${#torrent_ids_array[@]} -eq 0 ]]; then
        log_info "No torrents found to process"
    else
        log_info "Torrent IDs to process: ${torrent_ids_array[*]}"
    fi
    for id in "${torrent_ids_array[@]}"; do
        # Skip empty IDs
        if [[ -z "$id" ]]; then
            log_debug "Skipping empty torrent ID"
            continue
        fi
        
        log_debug "Processing torrent ID: $id"
        local info
        info=$(get_torrent_info "${id}")
        
        local hash
        hash=$(grep "Hash:" <<< "${info}" | awk '{print $2}')
        local ratio
        ratio=$(grep "Ratio:" <<< "${info}" | awk '{print $2 == "None" ? 0 : $2}' | tr -cd '0-9.')
        ratio=${ratio:-0}
        local time
        time=$(grep "Seeding Time:" <<< "${info}" | awk '{print $3 == "None" ? 0 : $3}' | tr -cd '0-9.')
        time=${time:-0}
        local percent_done
        percent_done=$(grep "Percent Done:" <<< "${info}" | awk '{gsub(/%/, ""); print $3 == "None" ? 0 : $3}')
        percent_done=${percent_done:-0}
        
        # Extract Transmission-reported directory and translate to local path.
        local reported_dir
        reported_dir=$(grep -i "Location:" <<< "${info}" | awk -F": " '{print $2}' | xargs)
        log_debug "Raw reported directory: '${reported_dir}'"
        
        # If the reported directory is empty, try to derive it from the name
        if [[ -z "${reported_dir}" ]]; then
            local name
            name=$(grep -i "Name:" <<< "${info}" | awk -F": " '{print $2}' | xargs)
            log_debug "Torrent name: '${name}'"
            
            # Check if there are labels we can use
            local labels
            labels=$(grep -i "Labels:" <<< "${info}" | awk -F": " '{print $2}' | xargs)
            log_debug "Torrent labels: '${labels}'"
            
            if [[ "${labels}" == *"Books"* ]]; then
                reported_dir="/downloads/Books"
            elif [[ "${labels}" == *"Movies"* ]]; then
                reported_dir="/downloads/Movies"
            elif [[ "${labels}" == *"TV"* ]]; then
                reported_dir="/downloads/TV"
            elif [[ "${labels}" == *"Games"* ]]; then
                reported_dir="/downloads/Games"
            elif [[ "${labels}" == *"Apps"* ]]; then
                reported_dir="/downloads/Apps"
            elif [[ "${labels}" == *"Music"* ]]; then
                reported_dir="/downloads/Music"
            else
                # Default to Other if we can't determine
                reported_dir="/downloads/Other"
            fi
            log_debug "Derived directory from labels: '${reported_dir}'"
        fi
        
        local dir
        dir=$(translate_source "${reported_dir}")
        log_info "Torrent source directory: '${reported_dir}' translated to '${dir}'"
        
        # Initialize empty directory mapping if needed
        if [[ -z "$dir" ]]; then
            log_warn "Empty directory path detected, using default"
            dir="${LOCAL_PATH_PREFIX}/Other"
        fi
        
        local dst
        dst=$(get_destination "${dir}")
        
        # Detect same-path mappings (different mounts)
        if [[ "${dir}" != "${dst}" && "${dir}" =~ ^/mnt/dsnas2/ && "${dst}" =~ ^/mnt/dsnas1/ ]]; then
            local dir_suffix="${dir#/mnt/dsnas2/}"
            local dst_suffix="${dst#/mnt/dsnas1/}"
            if [[ "${dir_suffix}" == "${dst_suffix}" ]]; then
                log_info "Source and destination are the same logical location with different mounts: ${dir_suffix}"
                mark_processed "${hash}"
                continue  # Skip to next torrent
            fi
        fi
        
        # Initialize warned_dirs for this directory if needed
        if [[ -n "${dir}" ]]; then
            [[ -z "${warned_dirs["${dir}"]+x}" ]] && warned_dirs["${dir}"]=0
        fi

        # Avoid processing the same directory more than once.
        if [[ -n "${processed_source_dirs["${dir}"]+x}" ]]; then
            log_info "Directory ${dir} has already been processed; skipping copy for torrent ${id}"
        elif (( $(bc <<< "${percent_done} >= 100") )) && ! is_processed "${hash}"; then
            log_info "Processing completed torrent ${id} (${percent_done}% done)"
            if [[ "${dst}" == "${DEFAULT_DST}" ]] && [[ -n "${dir}" ]] && (( warned_dirs["${dir}"] == 0 )); then
                log_warn "Using default destination for: ${dir}"
                warned_dirs["${dir}"]=1
            fi
            local targets=("${dst}")
            case "${dst}" in
                "${DIR_MOVIES_DST}") 
                    targets+=("${STORAGE_DIRS_ARRAY[@]}")
                    ;;
                "${DIR_TV_DST}")
                    # If there are TV storage dirs, include them
                    [[ -n "${STORAGE_TV_DIRS}" ]] && IFS=',' read -ra TV_DIRS <<< "${STORAGE_TV_DIRS}" && targets+=("${TV_DIRS[@]}")
                    ;;
            esac
            
            if ! files_need_processing "${dir}" "${targets[@]}"; then
                log_info "Skipping copy - files already exist in:"
                for target in "${targets[@]}"; do
                    [[ -d "${target}" ]] && log_info "    - ${target}"
                done
            else
                process_copy "${id}" "${hash}" "${dir}" "${dst}"
                processed_source_dirs["${dir}"]=1
            fi
        fi

        if (( $(bc <<< "${ratio} >= ${SEED_RATIO}") )) || (( $(bc <<< "${time} >= ${SEED_TIME}") )); then
            log_info "Removing torrent ${id} (Ratio: ${ratio}, Time: ${time})"
            process_removal "${id}"
        fi
    done

    # Print count of processed directories
    if [[ "${DEBUG}" -eq 1 ]]; then
        log_debug "Processed source directories count: ${#processed_source_dirs[@]}"
        for dir in "${!processed_source_dirs[@]}"; do
            log_debug "Processed directory: $dir"
        done
    fi

    # Check disk usage for all directories
    for dir in "${REQUIRED_DIRS[@]}"; do
        check_disk_usage "${dir}"
    done
    for dir in "${STORAGE_DIRS_ARRAY[@]}"; do
        check_disk_usage "${dir}"
    done
}

######################
# Command-line Parsing #
######################
parse_args "$@"

if (( INTERACTIVE )); then
    read -rp "Confirm processing? (y/n) " choice
    [[ "${choice}" =~ ^[Yy]$ ]] || exit 0
fi

main