#!/usr/bin/env bash
set -Eeuo pipefail

SELF_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
BASE_DIR="${SELF_PATH%/*}"
BASHISMS_DIR="${BASE_DIR}/bashisms"

if [[ "${ARTIX_RUNTIME_DIR:-}" != "/tmp/artix-run" && -d /tmp ]]; then
    mkdir -p /tmp/artix-run
    cp -a "${BASE_DIR}" /tmp/artix-run/ 2>/dev/null || cp -r "${BASE_DIR}" /tmp/artix-run/
    export ARTIX_RUNTIME_DIR=/tmp/artix-run
    exec "/tmp/artix-run/${BASE_DIR##*/}/install" "$@"
    exit 0
fi

_die_early() {
    printf '\n\e[1;31m[!] %s\e[0m\n' "${1:-unknown error}" >&2
    exit 1
}

installer_abort() {
    clear
    printf '\n[!] Installation interrupted.\n' >&2
    printf '[!] The installation was NOT completed.\n' >&2
    exit 130
}

_generate_bug_report() {
    local report_dir="/tmp/artixforge-bugreport-$(date +%Y%m%d-%H%M%S)"
    mkdir -p "${report_dir}"
    
    [[ -f /tmp/artix-installer/install.log ]] && cp /tmp/artix-installer/install.log "${report_dir}/"
    [[ -f /tmp/artix-installer/state.conf ]] && cp /tmp/artix-installer/state.conf "${report_dir}/"
    [[ -f /tmp/artix-migration-debug.log ]] && cp /tmp/artix-migration-debug.log "${report_dir}/"
    [[ -f /tmp/artix-installer/logs/artix-debug.log ]] && cp /tmp/artix-installer/logs/artix-debug.log "${report_dir}/"
    [[ -f /tmp/artix-installer/logs/post-stage.log ]] && cp /tmp/artix-installer/logs/post-stage.log "${report_dir}/"
    
    if [[ -d /tmp/artix-installer/stages ]]; then
        cp -r /tmp/artix-installer/stages "${report_dir}/"
    fi

    {
        printf 'Artix Installer Bug Report\n'
        printf '=====================\n'
        printf 'Date: %s\n' "$(date)"
        printf 'Version: %s\n' "$(cat "${BASE_DIR}/VERSION" 2>/dev/null || echo unknown)"
        printf 'Kernel: %s\n' "$(uname -r)"
        printf '\nSystem Info:\n'
        uname -a
        printf '\nCPU:\n'
        lscpu 2>/dev/null | head -20 || true
        printf '\nMemory:\n'
        free -h 2>/dev/null || true
        printf '\nDisk:\n'
        lsblk -f 2>/dev/null || true
        printf '\nMounts:\n'
        mount 2>/dev/null | grep -E '/mnt|artix' || true
    } > "${report_dir}/system-info.txt"

    local tarball="${report_dir}.tar.gz"
    tar czf "${tarball}" -C /tmp "$(basename "${report_dir}")" 2>/dev/null || true
    rm -rf "${report_dir}"
    echo "${tarball}"
}

installer_error() {
    local rc="${1:-1}" line="${2:-unknown}"
    clear
    printf '\n[!] Installation failed at line: %s\n' "${line}" >&2
    printf '[!] Exit status: %s\n' "${rc}" >&2
    if [[ -f /tmp/artix-installer/install.log ]]; then
        printf '\n[!] Last 10 lines of install log:\n' >&2
        tail -10 /tmp/artix-installer/install.log >&2
    fi
    if [[ -f /tmp/artix-migration-debug.log ]]; then
        printf '\n[!] Migration debug log available at: /tmp/artix-migration-debug.log\n' >&2
        printf '[!] Last 20 lines:\n' >&2
        tail -20 /tmp/artix-migration-debug.log >&2
    fi
    
    local report
    report=$(_generate_bug_report 2>/dev/null || true)
    if [[ -n "${report}" ]]; then
        printf '\n[!] Bug report generated: %s\n' "${report}" >&2
        printf '[!] Include this file when reporting issues.\n' >&2
    fi
    
    printf '\n[!] The installation was NOT completed.\n' >&2
    exit "${rc}"
}

self_update_installer() {
    local -a restart_args=("$@")
    local repo_url='https://github.com/realvolk/ArtixForge.git' tmp_dir
    tmp_dir="$(mktemp -d)"
    printf '[*] Downloading latest installer...\n'
    if ! git clone --depth 1 "${repo_url}" "${tmp_dir}" >/dev/null 2>&1; then
        printf '[!] Failed to download latest installer.\n'
        rm -rf "${tmp_dir}"
        return 1
    fi
    if [[ ! -f "${tmp_dir}/install" ]]; then
        printf '[!] Invalid installer payload received.\n'
        rm -rf "${tmp_dir}"
        return 1
    fi
    printf '[*] Replacing old installer files...\n'
    find "${BASE_DIR}" -mindepth 1 -maxdepth 1 ! -name '.git' ! -name 'install' -exec rm -rf {} +
    cp -a "${tmp_dir}/." "${BASE_DIR}/"
    chmod +x "${BASE_DIR}/install"
    rm -rf "${tmp_dir}"
    printf '[*] Restarting updated installer...\n'
    sleep 1
    exec "${BASE_DIR}/install" "${restart_args[@]}"
}

check_installer_version() {
    local local_version remote_version
    [[ -f "${BASE_DIR}/VERSION" ]] || return 0
    local_version="$(tr -d '[:space:]' < "${BASE_DIR}/VERSION")"
    remote_version="$(curl -fsSL 'https://raw.githubusercontent.com/realvolk/ArtixForge/main/VERSION' 2>/dev/null | tr -d '[:space:]' || true)"
    [[ -n "${remote_version}" ]] || return 0
    if [[ "${local_version}" == "${remote_version}" ]]; then return 0; fi
    if [[ "$(printf '%s\n%s\n' "${local_version}" "${remote_version}" | sort -V | tail -n1)" != "${remote_version}" ]]; then return 0; fi
    if tui_yesno "Installer Outdated" "Installed: ${local_version}\nLatest: ${remote_version}\n\nUpdate now?"; then
        if self_update_installer "$@"; then exit 0; fi
        if ! tui_yesno "Update Failed" "Continue anyway?"; then clear; printf '[*] Installation cancelled.\n'; exit 0; fi
    else
        if ! tui_yesno "Continue Anyway" "This installer may contain bugs already fixed upstream.\nContinue using the outdated version?"; then clear; printf '[*] Installation cancelled.\n'; exit 0; fi
    fi
}

verify_installer_layout() {
    local required_files=(
        "${BASHISMS_DIR}/common/common.sh"
        "${BASHISMS_DIR}/state/state.sh"
        "${BASHISMS_DIR}/common/kernels.sh"
        "${BASHISMS_DIR}/packages/resolve.sh"
        "${BASHISMS_DIR}/packages/install.sh"
    )
    local required_dirs=(
        "${BASHISMS_DIR}/tui"
        "${BASHISMS_DIR}/installer/stages"
        "${BASHISMS_DIR}/installer/storage"
        "${BASHISMS_DIR}/installer/install"
        "${BASHISMS_DIR}/installer/post"
        "${BASHISMS_DIR}/recovery"
        "${BASHISMS_DIR}/iso"
        "${BASHISMS_DIR}/migrations"
        "${BASHISMS_DIR}/poweruser"
        "${BASHISMS_DIR}/packages"
        "${BASHISMS_DIR}/packages/catalog"
    )
    local file dir
    for file in "${required_files[@]}"; do [[ -f "${file}" ]] || return 1; done
    for dir in "${required_dirs[@]}"; do [[ -d "${dir}" ]] || return 1; done
    [[ -d "${BASHISMS_DIR}/tui/menus" ]] || return 1
    return 0
}

source_tree() {
    local dir file
    for dir in "$@"; do
        [[ -d "${dir}" ]] || { printf '[!] missing directory: %s\n' "${dir}" >&2; return 1; }
        while IFS= read -r -d '' file; do
            source "${file}" || { printf '[!] Failed to source: %s\n' "${file}" >&2; return 1; }
        done < <(find "${dir}" -maxdepth 1 -type f -name '*.sh' -print0 | sort -z)
    done
}

trap 'installer_abort' INT TERM
trap 'installer_error $? ${LINENO}' ERR

if ! verify_installer_layout; then
    printf '[*] Installer layout is incomplete.\n'
    printf '[*] Attempting automatic repair...\n'
    if ! self_update_installer; then
        _die_early 'failed to repair installer layout'
    fi
    exit 0
fi

source "${BASHISMS_DIR}/common/common.sh"
source "${BASHISMS_DIR}/state/state.sh"
source "${BASHISMS_DIR}/common/kernels.sh"
source "${BASHISMS_DIR}/common/yaml.sh"
source_tree "${BASHISMS_DIR}/packages/catalog"
source "${BASHISMS_DIR}/packages/resolve.sh"
source "${BASHISMS_DIR}/packages/install.sh"
source "${BASHISMS_DIR}/installer/iso-profiles.sh"
source "${BASHISMS_DIR}/iso/tui.sh" 2>/dev/null || true
source_tree \
    "${BASHISMS_DIR}/tui" \
    "${BASHISMS_DIR}/installer/storage" \
    "${BASHISMS_DIR}/installer/install" \
    "${BASHISMS_DIR}/installer/post" \
    "${BASHISMS_DIR}/installer/stages"

MODE=""
DEBUG="false"
ARTIX_BOOT_MODE=""

detect_boot_mode() {
    if [[ -d /sys/firmware/efi ]]; then
        ARTIX_BOOT_MODE="uefi"
        log_info "UEFI boot detected"
    else
        ARTIX_BOOT_MODE="bios"
        log_info "BIOS/Legacy boot detected – UEFI features disabled"
    fi
    export ARTIX_BOOT_MODE
    state_set ARTIX_BOOT_MODE "${ARTIX_BOOT_MODE}"
}

_artix_welcome() {
    tui_msg_quick "Artix Linux" "
   __________________________
 < Simple. Fast. Systemd-free. >
  --------------------------
         \\    .--.
          \\  |o_o |
             |:_/ |
            //   \\ \\
           (|     | )
          /'\\_   _/\`\\
          \\___)=(___/
"
}

_verify_root_password() {
    local attempt
    attempt=$(tui_password "Root Password Required" "Enter root password to unlock advanced features:") || return 1
    if [[ -z "${attempt}" ]]; then
        return 1
    fi

    if [[ -f /etc/shadow ]]; then
        local root_hash
        root_hash=$(grep '^root:' /etc/shadow | cut -d: -f2)
        if [[ -n "${root_hash}" && "${root_hash}" != "!" && "${root_hash}" != "*" ]]; then
            local check
            check=$(openssl passwd -6 -- "${attempt}" 2>/dev/null || true)
            if [[ "${check}" == "${root_hash}" ]]; then
                return 0
            fi
            return 1
        fi
    fi

    if [[ "${EUID}" -eq 0 ]]; then
        return 0
    fi

    return 1
}

_validate_post_install_script() {
    local script_path
    script_path="$(state_get POST_INSTALL_SCRIPT '')"
    [[ -n "${script_path}" ]] || return 0
    
    if [[ ! -f "${script_path}" ]]; then
        log_error "POST_INSTALL_SCRIPT '${script_path}' does not exist"
        return 1
    fi
    
    if [[ ! -r "${script_path}" ]]; then
        log_error "POST_INSTALL_SCRIPT '${script_path}' is not readable"
        return 1
    fi

    log_info "Post-install script validated: ${script_path}"
    return 0
}

main_menu() {
    DEBUG='false'

    if tui_yesno "Debug Mode" "Enable debug mode (verbose logging)?"; then
        DEBUG='true'
    else
        DEBUG='false'
    fi

    while true; do
        local -a menu_items=(
            "Installation – guided setup"
        )

        [[ -f /tmp/artix-installer/migration-stage.conf ]] && menu_items+=("Resume Migration – continue interrupted migration")
        [[ -f /tmp/artix-installer/iso-build-stage.conf ]] && menu_items+=("Resume ISO Build – continue interrupted ISO build")
        [[ -f "${STATE_FILE}" ]] && grep -q '^DISK=' "${STATE_FILE}" 2>/dev/null && menu_items+=("Resume Installation – continue interrupted install")

        menu_items+=("Advanced – recovery, power user, migration, ISO")

        local mode
        mode=$(tui_menu "Artix Installer" "Select installation mode:" "${menu_items[@]}") || exit 1

        case "${mode}" in
            Installation*)  MODE='auto' ; break ;;
            Advanced*)
                if ! _verify_root_password; then
                    tui_msg_quick "Access Denied" "Incorrect root password."
                    continue
                fi
                local advanced_choice
                advanced_choice=$(tui_menu "Advanced" "Select advanced mode:" \
                    "Recovery – scan /mnt for existing system" \
                    "Power User – source-built packages" \
                    "System Migration – convert init or desktop" \
                    "Build ISO – create a custom live ISO" \
                    "Back") || continue
                case "${advanced_choice}" in
                    Recovery*)          MODE='recovery' ; break ;;
                    Power*)             MODE='power' ; break ;;
                    "System Migration"*) MODE='migrate' ; break ;;
                    "Build ISO"*)       MODE='iso' ; break ;;
                    *)                  continue ;;
                esac
                ;;
            *Migration*)    MODE='migrate' ; break ;;
            *ISO*)          MODE='iso' ; break ;;
            *Installation*) MODE='resume' ; break ;;
            *)              MODE='auto' ; break ;;
        esac
    done

    tui_msg_quick "Mode Selected" "Mode: ${MODE}   Debug: ${DEBUG}"
}

run_install_pipeline() {
    local lint_errors
    lint_errors=$(lint_state 2>/dev/null || true)
    if [[ -n "${lint_errors}" ]]; then
        tui_msg "State Validation Failed" "$(printf 'The state file has errors:\n\n%s\n\nCannot proceed.' "${lint_errors}")"
        return 1
    fi
    
    if ! _validate_post_install_script; then
        return 1
    fi

    stage_preflight || die 'preflight stage failed'
    stage_storage   || die 'storage stage failed'
    stage_base      || die 'base stage failed'
    [[ "$(state_get POWER_USER no)" == "yes" ]] && stage_poweruser || true
    stage_payload   || die 'payload stage failed'
    stage_chroot    || die 'chroot stage failed'
    stage_init      || die 'init configuration stage failed'
    stage_post      || die 'post-install stage failed'
    stage_finalize  || die 'finalization stage failed'
}

run_migration() {
    local mig_type mig_src mig_tgt
    mig_type="$(state_get MIGRATION_TYPE '')"
    mig_src="$(state_get MIGRATION_SRC '')"
    mig_tgt="$(state_get MIGRATION_TGT '')"
    [[ -n "${mig_type}" && -n "${mig_src}" && -n "${mig_tgt}" ]] || die "Migration state incomplete"
    log_info "Running migration: ${mig_type} ${mig_src} → ${mig_tgt}"
    case "${mig_type}" in
        ata)
            [[ -f "${BASHISMS_DIR}/migrations/ata/ata-migrate.sh" ]] || die "ATA migration module not found"
            source "${BASHISMS_DIR}/migrations/ata/ata-migrate.sh"
            ata_migrate_main
            ;;
        init)
            source "${BASHISMS_DIR}/migrations/inits/common.sh"
            run_init_migration "${mig_src}" "${mig_tgt}"
            ;;
        desktop)
            source "${BASHISMS_DIR}/migrations/des/common.sh"
            run_de_migration "${mig_src}" "${mig_tgt}"
            ;;
        *) die "Unknown migration type: ${mig_type}" ;;
    esac
}

start_iso_build() {
    if [[ -d /run/artix/sfs/rootfs ]]; then
        tui_msg_quick "Not Supported" "ISO building from a live environment is not supported."
        return 0
    fi

    if ! command -v buildiso &>/dev/null; then
        if tui_yesno "Missing Tools" "artools is not installed. Install now?"; then
            pacman -S --noconfirm artools iso-profiles || die "Failed to install artools"
            modprobe loop
        else
            die "artools is required for ISO generation"
        fi
    fi

    source "${BASHISMS_DIR}/iso/tui.sh"

    tui_iso_hub || return 1

    if [[ "$(state_get ALLOW_OFFLINE no)" == "yes" && "$(state_get ISO_BOOT_MODE live)" == "live" ]]; then
        tui_iso_target_config
    fi

    local profile_name init kernel offline boot_mode iso_output_dir
    profile_name="$(state_get QUICK_PROFILE Custom)"
    init="$(state_get INIT openrc)"
    kernel="$(state_get KERNEL_CHOICE linux)"
    offline="$(state_get ALLOW_OFFLINE no)"
    boot_mode="$(state_get ISO_BOOT_MODE live)"
    iso_output_dir="$(state_get ISO_OUTPUT_DIR "${HOME}/ArtixForge-ISO")"

    source "${BASHISMS_DIR}/iso/build.sh"
    build_artix_iso "${profile_name}" "${init}" "${kernel}" "${offline}" "${boot_mode}" "${iso_output_dir}"
}

start_auto_install() {
    rm -f /tmp/artix-installer/state.conf
    local var
    for var in DISK FS_TYPE BOOTLOADER KERNEL_CHOICE INIT WM_DE \
               NETWORK_STACK AUDIO_STACK PRIV_ESCALATION HOSTNAME \
               TIMEZONE LOCALE KEYMAP SWAP_ENABLED SWAP_SIZE ZRAM_PERCENT \
               USE_LUKS USE_LVM GENERATE_UKI ENABLE_ARCH_REPOS POWER_USER \
               EXTRAS DISPLAY_MANAGER X_STACK MICROCODE_OVERRIDE USER_SHELL \
               USER_COUNT ROOT_PASS LUKS_PASS POST_INSTALL_SCRIPT; do
        unset "${var}"
    done
    tui_collect_install_config

    state_save
    rm -f /tmp/artix-installer/stages/*.done 2>/dev/null || true
    run_install_pipeline
}

resume_install() {
    if [[ -f /tmp/artix-installer/migration-stage.conf ]]; then
        log_info "Resuming migration..."
        state_load
        run_migration
        return 0
    fi
    if [[ -f /tmp/artix-installer/iso-build-stage.conf ]]; then
        log_info "Resuming ISO build..."
        state_load
        start_iso_build
        return 0
    fi
    [[ -f "${STATE_FILE}" ]] || die 'no saved installer state found'
    state_load
    [[ -n "${DISK:-}" ]] || die 'saved installer state is incomplete'
    run_install_pipeline
}

start_recovery_install() {
    local recovery_status
    if ! recovery_detect_install; then
        tui_msg "Recovery Mode" "No installation found at /mnt."
        if tui_yesno "Auto-mount" "Attempt to automatically unlock LUKS, activate LVM, and mount the installation?"; then
            recovery_mount_all
        else
            clear; tui_msg_quick "Recovery Cancelled" "Recovery cancelled by user."; exit 0
        fi
    fi

    recovery_import_state
    recovery_status="$(recovery_get_status)"

    if ! tui_yesno "Recovery Mode" \
"Detected an existing installation mounted at /mnt.

${recovery_status}

This usually means a previous installation
was interrupted or partially completed.

Attempt recovery and continue installation?"; then
        clear; tui_msg_quick "Recovery Cancelled" "Recovery cancelled by user."; exit 0
    fi
    log_info "Entering recovery mode..."

    while true; do
        tui_recovery_hub || break
        
        if tui_yesno "Continue" "Recovery action completed. Continue with more repairs or proceed?"; then
            continue
        fi
        break
    done

    if tui_yesno "Full Reinstall" "Would you like to start a fresh installation instead?"; then
        rm -f /tmp/artix-installer/state.conf
        rm -f /tmp/artix-installer/stages/*.done 2>/dev/null || true
        umount -R /mnt 2>/dev/null || true
        MODE='auto'
        start_auto_install
        return
    fi

    clear
    tui_msg_quick "Recovery" "Recovery mode exited. System remains at /mnt."
}

start_system_migration() {
    if [[ -f "${BASHISMS_DIR}/migrations/migrate.sh" ]]; then
        source "${BASHISMS_DIR}/migrations/migrate.sh"
        tui_migration_menu
    else
        tui_msg_quick "Not Available" "Migration module not found at bashisms/migrations/migrate.sh"
    fi
}

main() {
    ensure_dirs
    require_root
    detect_boot_mode

    if ! command -v gum &>/dev/null; then
        if [[ -f "${BASE_DIR}/bin/gum" ]]; then
            install -Dm755 "${BASE_DIR}/bin/gum" /usr/local/bin/gum
        else
            printf '[*] Gum not found. Installing...\n'
            pacman -Sy --noconfirm gum 2>/dev/null || {
                printf '[!] Failed to install gum. Install it manually and retry.\n' >&2
                exit 1
            }
        fi
    fi

    if ! command -v jq &>/dev/null; then
        printf '[*] jq not found. Installing...\n'
        pacman -Sy --noconfirm jq 2>/dev/null || {
            printf '[!] Failed to install jq. Install it manually and retry.\n' >&2
            exit 1
        }
    fi

    iso_profiles_validate || true
    _artix_welcome
    sleep 1

    clear
    main_menu

    if [[ "${DEBUG}" == 'true' ]]; then
        export ARTIX_DEBUG='true'
        mkdir -p /tmp/artix-installer/logs
        exec 19> "/tmp/artix-installer/logs/artix-debug.log"
        export BASH_XTRACEFD=19
        export PS4='+ ${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]}: '
        set -x
        trap 'printf "!!! ERROR at %s:%s in %s (rc=%s)\n" "${BASH_SOURCE##*/}" "${LINENO}" "${FUNCNAME[0]:-main}" "$?" >&19' ERR
    fi

    check_installer_version

    case "${MODE}" in
        auto)     start_auto_install ;;
        resume)   resume_install ;;
        recovery) start_recovery_install ;;
        power)    MODE='power' ; state_set POWER_USER "yes" ; start_auto_install ;;
        migrate)  start_system_migration ;;
        iso)      start_iso_build ;;
        *)        printf '\e[1;31mNo mode selected. Exiting.\e[0m\n'; exit 1 ;;
    esac
}

main "$@"