#!/bin/bash

MTPHOTOS_COMPOSE_CHANGED=0

mtphotos_error() {
    local message="$1"
    if [ -n "${TRIM_TEMP_LOGFILE:-}" ]; then
        printf '%s\n' "$message" > "${TRIM_TEMP_LOGFILE}"
    fi
    printf 'ERROR: %s\n' "$message" >&2
}

mtphotos_validate_storage_paths() {
    local required="${1:-false}"
    local config_path="${wizard_config_dir:-}"
    local upload_path="${wizard_upload_dir:-}"
    local label
    local path

    if [ -z "$config_path" ] && [ -z "$upload_path" ]; then
        if [ "$required" = true ]; then
            mtphotos_error "fnOS 未提供安装向导中的 config 和 upload 路径。"
            return 1
        fi
        return 0
    fi
    if [ -z "$config_path" ] || [ -z "$upload_path" ]; then
        mtphotos_error "config 和 upload 的 NAS 路径必须同时设置。"
        return 1
    fi
    if [ "$config_path" = "$upload_path" ]; then
        mtphotos_error "config 和 upload 不能使用同一个 NAS 路径。"
        return 1
    fi

    for label in config upload; do
        if [ "$label" = "config" ]; then path="$config_path"; else path="$upload_path"; fi
        if ! printf '%s' "$path" | LC_ALL=C grep -Eq '^/vol[0-9]+/.+'; then
            mtphotos_error "$label 必须使用 /vol数字/目录 格式的 NAS 绝对路径。"
            return 1
        fi
        case "$path" in
            *//*|*/./*|*/../*|*/.|*/..|*$'\n'*|*$'\r'*)
                mtphotos_error "$label 路径不能包含重复分隔符、换行符或 .、.. 路径段。"
                return 1
                ;;
        esac
    done
}

mtphotos_validate_gpu_support() {
    case "${wizard_gpu_support:-none}" in
        none|intel|nvidia) return 0 ;;
        *)
            mtphotos_error "无效的显卡支持选项。"
            return 1
            ;;
    esac
}

mtphotos_yaml_escape() {
    printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\$/$$/g'
}

mtphotos_mount_lines() {
    local paths="$1"
    local path
    local escaped

    [ -z "$paths" ] && return 0
    case "$paths" in
        *$'\n'*|*$'\r'*|*$'\302\205'*|*$'\342\200\250'*|*$'\342\200\251'*)
            mtphotos_error "授权目录不能包含换行符。"
            return 1
            ;;
    esac
    if printf '%s' "$paths" | LC_ALL=C grep -q '[[:cntrl:]]'; then
        mtphotos_error "授权目录不能包含控制字符。"
        return 1
    fi

    while IFS= read -r path; do
        [ -z "$path" ] && continue
        while [ "$path" != "/" ] && [ "${path%/}" != "$path" ]; do path="${path%/}"; done
        case "$path" in
            /*) ;;
            *) mtphotos_error "授权目录必须是绝对路径：$path"; return 1 ;;
        esac
        case "$path" in
            /|*//*|*/./*|*/../*|*/.|*/..)
                mtphotos_error "授权目录不能是根目录，也不能包含重复分隔符或 .、.. 路径段：$path"
                return 1
                ;;
        esac
        case "$MTPHOTOS_MOUNT_TARGETS" in *$'\n'"$path"$'\n'*) continue ;; esac
        MTPHOTOS_MOUNT_TARGETS+="$path"$'\n'
        escaped=$(mtphotos_yaml_escape "$path") || return 1
        printf '      - "%s:%s"\n' "$escaped" "$escaped" || return 1
    done <<EOF
$(printf '%s' "$paths" | tr ':' '\n')
EOF
}

mtphotos_required_volume_lines() {
    local config_path="${wizard_config_dir:-./data/config}"
    local upload_path="${wizard_upload_dir:-./data/upload}"
    local escaped_config
    local escaped_upload

    escaped_config=$(mtphotos_yaml_escape "$config_path") || return 1
    escaped_upload=$(mtphotos_yaml_escape "$upload_path") || return 1
    printf '      - "%s:/config"\n' "$escaped_config" || return 1
    printf '      - "%s:/upload"\n' "$escaped_upload" || return 1
}

mtphotos_timezone_lines() {
    local timezone="${wizard_timezone:-Asia/Shanghai}"
    local escaped
    escaped=$(mtphotos_yaml_escape "$timezone") || return 1
    printf '      TZ: "%s"\n' "$escaped"
}

mtphotos_gpu_support_lines() {
    case "${wizard_gpu_support:-none}" in
        none) ;;
        intel)
            printf '%s\n' '    devices:'
            printf '%s\n' '      - "/dev/dri:/dev/dri"'
            ;;
        nvidia)
            printf '%s\n' '    runtime: nvidia'
            ;;
        *)
            mtphotos_error "无效的显卡支持选项。"
            return 1
            ;;
    esac
}

mtphotos_environment_lines() {
    local input="$1"
    local entry
    local key
    local value
    local escaped
    local seen=$'\n'

    [ -z "$input" ] && return 0
    case "$input" in
        *$'\n'*|*$'\r'*|*$'\302\205'*|*$'\342\200\250'*|*$'\342\200\251'*)
            mtphotos_error "附加环境变量不能包含换行符。"
            return 1
            ;;
    esac
    if printf '%s' "$input" | LC_ALL=C grep -q '[[:cntrl:]]'; then
        mtphotos_error "附加环境变量不能包含控制字符。"
        return 1
    fi

    while IFS= read -r entry; do
        [ -z "$entry" ] && continue
        case "$entry" in *=*) ;; *) mtphotos_error "附加环境变量必须使用 KEY=value 格式：$entry"; return 1 ;; esac
        key="${entry%%=*}"
        value="${entry#*=}"
        case "$key" in
            ""|[0-9]*|*[!A-Za-z0-9_]*) mtphotos_error "无效的环境变量名称：$key"; return 1 ;;
            TZ) mtphotos_error "请使用时区字段设置 TZ。"; return 1 ;;
        esac
        case "$seen" in *$'\n'"$key"$'\n'*) mtphotos_error "环境变量名称重复：$key"; return 1 ;; esac
        seen+="$key"$'\n'
        escaped=$(mtphotos_yaml_escape "$value") || return 1
        printf '      %s: "%s"\n' "$key" "$escaped" || return 1
    done <<EOF
$(printf '%s' "$input" | tr ';' '\n')
EOF
}

mtphotos_find_incoming_compose() {
    local base
    local relative
    local candidate
    local discovered

    for base in "${TRIM_PKGINST_TEMP_DIR:-}" "${TRIM_TEMP_TPKFILE:-}"; do
        [ -n "$base" ] || continue
        for relative in docker/docker-compose.yaml app/docker/docker-compose.yaml target/docker/docker-compose.yaml; do
            candidate="${base%/}/$relative"
            if [ -f "$candidate" ]; then printf '%s\n' "$candidate"; return 0; fi
        done
        discovered=$(find "$base" -maxdepth 5 -type f -path '*/docker/docker-compose.yaml' -print -quit 2>/dev/null || true)
        if [ -n "$discovered" ]; then printf '%s\n' "$discovered"; return 0; fi
    done
    mtphotos_error "找不到待安装的 Docker Compose 文件。"
    return 1
}

mtphotos_accessible_paths() {
    local policy="$1"
    local state_file
    local paths

    if [ "$policy" = "none" ]; then
        printf '%s' "${TRIM_DATA_ACCESSIBLE_PATHS:-}"
        return 0
    fi
    if [ -z "${TRIM_PKGVAR:-}" ]; then
        mtphotos_error "fnOS 未提供 TRIM_PKGVAR。"
        return 1
    fi
    state_file="${TRIM_PKGVAR}/authorized-paths"
    if [ "${TRIM_DATA_ACCESSIBLE_PATHS+x}" = x ]; then
        paths="${TRIM_DATA_ACCESSIBLE_PATHS}"
        if ! mkdir -p "${TRIM_PKGVAR}" || ! (umask 077 && printf '%s' "$paths" > "${state_file}.tmp.$$") || ! mv -f "${state_file}.tmp.$$" "$state_file"; then
            rm -f "${state_file}.tmp.$$"
            mtphotos_error "无法保存授权目录状态。"
            return 1
        fi
        printf '%s' "$paths"
    elif [ -f "$state_file" ]; then
        cat "$state_file"
    fi
}

mtphotos_sync_compose() {
    local compose_file="$1"
    local policy="${2:-auto}"
    local paths
    local temporary_file
    local line
    local volume_state=before
    local environment_state=before
    local required_volume_state=before
    local timezone_state=before
    local gpu_support_state=before

    MTPHOTOS_COMPOSE_CHANGED=0
    [ -f "$compose_file" ] || { mtphotos_error "找不到 Docker Compose 文件：$compose_file"; return 1; }
    paths=$(mtphotos_accessible_paths "$policy") || return 1
    temporary_file=$(mktemp /tmp/mtphotos-compose.XXXXXX) || { mtphotos_error "无法创建 Docker Compose 临时文件。"; return 1; }

    while IFS= read -r line || [ -n "$line" ]; do
        case "$line" in
            *"# mtphotos-gpu-support-begin")
                [ "$gpu_support_state" = before ] || { rm -f "$temporary_file"; mtphotos_error "显卡支持标记重复或顺序错误。"; return 1; }
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                mtphotos_gpu_support_lines >> "$temporary_file" || { rm -f "$temporary_file"; return 1; }
                gpu_support_state=inside
                ;;
            *"# mtphotos-gpu-support-end")
                [ "$gpu_support_state" = inside ] || { rm -f "$temporary_file"; mtphotos_error "显卡支持标记重复或顺序错误。"; return 1; }
                gpu_support_state=after
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                ;;
            *"# mtphotos-required-volumes-begin")
                [ "$required_volume_state" = before ] || { rm -f "$temporary_file"; mtphotos_error "必需目录标记重复或顺序错误。"; return 1; }
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                mtphotos_required_volume_lines >> "$temporary_file" || { rm -f "$temporary_file"; return 1; }
                required_volume_state=inside
                ;;
            *"# mtphotos-required-volumes-end")
                [ "$required_volume_state" = inside ] || { rm -f "$temporary_file"; mtphotos_error "必需目录标记重复或顺序错误。"; return 1; }
                required_volume_state=after
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                ;;
            *"# mtphotos-authorized-volumes-begin")
                [ "$volume_state" = before ] || { rm -f "$temporary_file"; mtphotos_error "授权目录标记重复或顺序错误。"; return 1; }
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                MTPHOTOS_MOUNT_TARGETS=$'\n/config\n/upload\n'
                mtphotos_mount_lines "$paths" >> "$temporary_file" || { rm -f "$temporary_file"; return 1; }
                volume_state=inside
                ;;
            *"# mtphotos-authorized-volumes-end")
                [ "$volume_state" = inside ] || { rm -f "$temporary_file"; mtphotos_error "授权目录标记重复或顺序错误。"; return 1; }
                volume_state=after
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                ;;
            *"# mtphotos-timezone-begin")
                [ "$timezone_state" = before ] || { rm -f "$temporary_file"; mtphotos_error "时区标记重复或顺序错误。"; return 1; }
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                mtphotos_timezone_lines >> "$temporary_file" || { rm -f "$temporary_file"; return 1; }
                timezone_state=inside
                ;;
            *"# mtphotos-timezone-end")
                [ "$timezone_state" = inside ] || { rm -f "$temporary_file"; mtphotos_error "时区标记重复或顺序错误。"; return 1; }
                timezone_state=after
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                ;;
            *"# mtphotos-extra-environment-begin")
                [ "$environment_state" = before ] || { rm -f "$temporary_file"; mtphotos_error "环境变量标记重复或顺序错误。"; return 1; }
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                mtphotos_environment_lines "${wizard_extra_environment:-}" >> "$temporary_file" || { rm -f "$temporary_file"; return 1; }
                environment_state=inside
                ;;
            *"# mtphotos-extra-environment-end")
                [ "$environment_state" = inside ] || { rm -f "$temporary_file"; mtphotos_error "环境变量标记重复或顺序错误。"; return 1; }
                environment_state=after
                printf '%s\n' "$line" >> "$temporary_file" || return 1
                ;;
            *)
                if [ "$gpu_support_state" != inside ] && [ "$required_volume_state" != inside ] && [ "$volume_state" != inside ] && [ "$timezone_state" != inside ] && [ "$environment_state" != inside ]; then
                    printf '%s\n' "$line" >> "$temporary_file" || return 1
                fi
                ;;
        esac
    done < "$compose_file"

    if [ "$gpu_support_state" != after ] || [ "$required_volume_state" != after ] || [ "$volume_state" != after ] || [ "$timezone_state" != after ] || [ "$environment_state" != after ]; then
        rm -f "$temporary_file"
        mtphotos_error "Docker Compose 文件缺少动态配置标记。"
        return 1
    fi
    if cmp -s "$temporary_file" "$compose_file"; then rm -f "$temporary_file"; return 0; fi
    if ! cp -f "$temporary_file" "$compose_file"; then
        rm -f "$temporary_file"
        mtphotos_error "无法提交 Docker Compose 配置；请确认应用生命周期以 root 身份运行。"
        return 1
    fi
    rm -f "$temporary_file"
    MTPHOTOS_COMPOSE_CHANGED=1
}

mtphotos_recreate_container() {
    local compose_file="$1"
    local compose_dir
    local compose_kind

    command -v docker >/dev/null 2>&1 || { mtphotos_error "找不到 Docker 命令，无法重建 MT Photos 容器。"; return 1; }
    compose_dir=$(cd "$(dirname "$compose_file")" && pwd) || { mtphotos_error "找不到 Docker Compose 目录。"; return 1; }

    if docker compose version >/dev/null 2>&1; then
        compose_kind=plugin
        if ! docker compose --project-name mtphotos --project-directory "$compose_dir" -f "$compose_file" config >/dev/null; then
            mtphotos_error "Docker Compose 配置校验失败，未删除原容器。"
            return 1
        fi
    elif command -v docker-compose >/dev/null 2>&1; then
        compose_kind=legacy
        if ! docker-compose --project-name mtphotos --project-directory "$compose_dir" -f "$compose_file" config >/dev/null; then
            mtphotos_error "Docker Compose 配置校验失败，未删除原容器。"
            return 1
        fi
    else
        mtphotos_error "找不到 docker compose 或 docker-compose，无法应用新的授权目录。"
        return 1
    fi

    # fnOS 创建的容器不一定带有 Compose project 标签；同名容器存在时，
    # compose up 无法接管，因此校验配置成功后再显式移除并重建。
    if docker inspect mt-photos >/dev/null 2>&1 && ! docker rm -f mt-photos >/dev/null; then
        mtphotos_error "无法移除旧的 MT Photos 容器。"
        return 1
    fi

    if [ "$compose_kind" = plugin ]; then
        if ! docker compose --project-name mtphotos --project-directory "$compose_dir" -f "$compose_file" up -d --force-recreate --remove-orphans; then
            mtphotos_error "Docker Compose 重建 MT Photos 容器失败，请查看 Docker 日志。"
            return 1
        fi
    elif ! docker-compose --project-name mtphotos --project-directory "$compose_dir" -f "$compose_file" up -d --force-recreate --remove-orphans; then
        mtphotos_error "Docker Compose 重建 MT Photos 容器失败，请查看 Docker 日志。"
        return 1
    fi

    if ! docker inspect mt-photos >/dev/null 2>&1; then
        mtphotos_error "Docker Compose 已执行，但未创建 MT Photos 容器。"
        return 1
    fi
}
