#!/bin/bash
set -euo pipefail
umask 022

REPO_NAME="${REPO_NAME:-breez}"
REPO_BASE="${REPO_BASE:-/srv/repo/${REPO_NAME}}"
LOCK_FILE="/tmp/repo-update-${REPO_NAME}.lock"
LOG_FILE="${LOG_FILE:-/var/log/repo-update.log}"
KEEP_VERSIONS="${KEEP_VERSIONS:-2}"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}"
}

# 防止重复运行
exec 200>"${LOCK_FILE}"
flock -n 200 || { log "另一个实例正在运行，退出"; exit 0; }

package_arch() {
    bsdtar -xOf "$1" .PKGINFO 2>/dev/null | awk -F ' = ' '$1 == "arch" { print $2; exit }'
}

reject_package() {
    local pkg="$1"
    local reason="$2"
    local source_dir
    source_dir="$(dirname "$pkg")"

    mkdir -p "${source_dir}/failed"
    mv -- "$pkg" "${source_dir}/failed/$(basename "$pkg")"
    [[ -f "${pkg}.sig" ]] && mv -- "${pkg}.sig" "${source_dir}/failed/$(basename "$pkg").sig"
    log "${reason}: $(basename "$pkg")"
}

# Files uploaded by repo-upload land here first. Dispatching from one shared
# directory keeps the client independent of the server's architecture list.
dispatch_uploads() {
    local upload_dir="${REPO_BASE}/incoming"
    local pkg pkg_arch arch_dir delivered

    [[ -d "${upload_dir}" ]] || return 0

    shopt -s nullglob
    local uploads=("${upload_dir}"/*.pkg.tar.{zst,xz,gz})
    shopt -u nullglob

    for pkg in "${uploads[@]}"; do
        if ! bsdtar -tf "$pkg" .PKGINFO &>/dev/null; then
            reject_package "$pkg" "[upload] 跳过损坏的包"
            continue
        fi

        pkg_arch="$(package_arch "$pkg")"
        if [[ -z "${pkg_arch}" ]]; then
            reject_package "$pkg" "[upload] 无法识别包架构"
            continue
        fi

        if [[ "${pkg_arch}" == "any" ]]; then
            delivered=0
            for arch_dir in "${REPO_BASE}"/*/; do
                [[ -d "${arch_dir}/incoming" && -d "${arch_dir}/pool" ]] || continue
                cp -- "$pkg" "${arch_dir}/incoming/"
                [[ -f "${pkg}.sig" ]] && cp -- "${pkg}.sig" "${arch_dir}/incoming/"
                delivered=1
            done
            if [[ ${delivered} -eq 0 ]]; then
                reject_package "$pkg" "[upload] 没有可用的架构目录"
                continue
            fi
        elif [[ -d "${REPO_BASE}/${pkg_arch}/incoming" ]]; then
            mv -- "$pkg" "${REPO_BASE}/${pkg_arch}/incoming/"
            if [[ -f "${pkg}.sig" ]]; then
                mv -- "${pkg}.sig" "${REPO_BASE}/${pkg_arch}/incoming/"
            fi
            log "[upload] 已分发 $(basename "$pkg") -> ${pkg_arch}"
            continue
        else
            reject_package "$pkg" "[upload] 未配置架构 ${pkg_arch}"
            continue
        fi

        rm -f -- "$pkg" "${pkg}.sig"
        log "[upload] 已分发 $(basename "$pkg") -> 所有架构"
    done
}

process_arch() {
    local ARCH="$1"
    local ARCH_DIR="${REPO_BASE}/${ARCH}"
    local INCOMING_DIR="${ARCH_DIR}/incoming"
    local POOL_DIR="${ARCH_DIR}/pool"
    local CHANGED=0

    mkdir -p "${INCOMING_DIR}/failed" "${POOL_DIR}"

    # --- 阶段 1: 处理 incoming 目录中的新包 ---
    shopt -s nullglob
    local new_pkgs=("${INCOMING_DIR}"/*.pkg.tar.{zst,xz,gz})
    shopt -u nullglob

    if [[ ${#new_pkgs[@]} -gt 0 ]]; then
        log "[${ARCH}] 发现 ${#new_pkgs[@]} 个新包待处理"
        for pkg in "${new_pkgs[@]}"; do
            local pkg_basename
            pkg_basename="$(basename "$pkg")"
            if ! bsdtar -tf "$pkg" .PKGINFO &>/dev/null; then
                reject_package "$pkg" "[${ARCH}] 跳过损坏的包"
                continue
            fi

            local detected_arch
            detected_arch="$(package_arch "$pkg")"
            if [[ "${detected_arch}" != "any" && "${detected_arch}" != "${ARCH}" ]]; then
                reject_package "$pkg" "[${ARCH}] 包架构 ${detected_arch:-unknown} 不匹配"
                continue
            fi

            mv -- "$pkg" "${POOL_DIR}/"
            chmod 0644 "${POOL_DIR}/${pkg_basename}"
            if [[ -f "${pkg}.sig" ]]; then
                mv -- "${pkg}.sig" "${POOL_DIR}/"
                chmod 0644 "${POOL_DIR}/${pkg_basename}.sig"
            fi
            log "[${ARCH}] ✓ 已接收: ${pkg_basename}"
            CHANGED=1
        done
    fi

    # --- 阶段 2: 检查 pool 目录是否有变化 ---
    local CHECKSUM_FILE="${ARCH_DIR}/.pool_checksum"
    local CURRENT_CHECKSUM
    CURRENT_CHECKSUM=$(find "${POOL_DIR}" -name '*.pkg.tar.*' ! -name '*.sig' \
        -printf '%f %s %T@\n' 2>/dev/null | sort | md5sum | cut -d' ' -f1)

    if [[ -f "${CHECKSUM_FILE}" ]]; then
        local PREVIOUS_CHECKSUM
        PREVIOUS_CHECKSUM=$(cat "${CHECKSUM_FILE}")
        [[ "${CURRENT_CHECKSUM}" != "${PREVIOUS_CHECKSUM}" ]] && CHANGED=1
    else
        CHANGED=1
    fi

    # --- 阶段 3: 有变化才重建数据库 ---
    if [[ ${CHANGED} -eq 1 ]]; then
        log "[${ARCH}] 开始重建仓库数据库..."

        # 清理旧版本
        command -v paccache &>/dev/null && \
            paccache -r -k "${KEEP_VERSIONS}" -c "${POOL_DIR}" 2>/dev/null || true

        # 重建符号链接（在 ARCH_DIR 根目录，指向 pool/）
        find "${ARCH_DIR}" -maxdepth 1 -name '*.pkg.tar.*' -type l -delete

        shopt -s nullglob
        for pkg in "${POOL_DIR}"/*.pkg.tar.{zst,xz,gz}; do
            local bname
            bname=$(basename "$pkg")
            ln -sf "pool/${bname}" "${ARCH_DIR}/${bname}"
            [[ -f "${pkg}.sig" ]] && ln -sf "pool/${bname}.sig" "${ARCH_DIR}/${bname}.sig"
        done
        shopt -u nullglob

        # 重建数据库
        cd "${ARCH_DIR}"
        rm -f "${REPO_NAME}".{db,files}{,.tar.gz,.old}

        shopt -s nullglob
        local db_pkgs=(*.pkg.tar.{zst,xz,gz})
        shopt -u nullglob

        if [[ ${#db_pkgs[@]} -gt 0 ]]; then
            repo-add "${REPO_NAME}.db.tar.gz" "${db_pkgs[@]}"
            log "[${ARCH}] ✓ 数据库已重建，包含 ${#db_pkgs[@]} 个包"
        else
            tar -czf "${REPO_NAME}.db.tar.gz" --files-from /dev/null
            cp -- "${REPO_NAME}.db.tar.gz" "${REPO_NAME}.files.tar.gz"
            ln -sf "${REPO_NAME}.db.tar.gz" "${REPO_NAME}.db"
            ln -sf "${REPO_NAME}.files.tar.gz" "${REPO_NAME}.files"
            log "[${ARCH}] ✓ 已创建空数据库"
        fi

        CURRENT_CHECKSUM=$(find "${POOL_DIR}" -name '*.pkg.tar.*' ! -name '*.sig' \
            -printf '%f %s %T@\n' 2>/dev/null | sort | md5sum | cut -d' ' -f1)
        echo "${CURRENT_CHECKSUM}" > "${CHECKSUM_FILE}"

        local TOTAL_SIZE
        TOTAL_SIZE=$(du -sh "${POOL_DIR}" | cut -f1)
        log "[${ARCH}] 仓库统计: ${#db_pkgs[@]} 个包, 总大小 ${TOTAL_SIZE}"
    else
        log "[${ARCH}] 无变化，跳过重建"
    fi
}

# First route packages submitted through the restricted SSH upload endpoint.
dispatch_uploads

# 自动发现并处理所有架构目录
for arch_entry in "${REPO_BASE}"/*/; do
    arch_name=$(basename "$arch_entry")
    [[ -d "${arch_entry}/incoming" && -d "${arch_entry}/pool" ]] || continue
    process_arch "$arch_name"
done
