#!/bin/bash

# ============================================================
#  Mihomo (Clash Meta) Terminal Manager
#  Features: Install / Subscription / Node list / Arrow-key switch / Proxy toggle
# ============================================================

MIHOMO_DIR="$HOME/.mihomo"
CONFIG_FILE="$MIHOMO_DIR/config.yaml"
DATA_FILE="$MIHOMO_DIR/.clash-data"
MIHOMO_BIN="$MIHOMO_DIR/mihomo"
PID_FILE="$MIHOMO_DIR/mihomo.pid"
LOG_FILE="$MIHOMO_DIR/mihomo.log"

API_PORT=9090
API_SECRET=""
API_BASE="http://127.0.0.1:$API_PORT"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
DIM='\033[2m'
BOLD='\033[1m'
REVERSE='\033[7m'
NC='\033[0m'

# ---- Helpers ----

print_banner() {
    echo -e "${CYAN}"
    echo "╔══════════════════════════════════════╗"
    echo "║     Mihomo Clash Terminal Manager    ║"
    echo "╚══════════════════════════════════════╝"
    echo -e "${NC}"
}

msg()  { echo -e "${GREEN}[✓]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
err()  { echo -e "${RED}[✗]${NC} $1"; }
info() { echo -e "${DIM}    $1${NC}"; }

save_data() {
    mkdir -p "$MIHOMO_DIR"
    cat > "$DATA_FILE" << EOF
SUB_URL="$SUB_URL"
MIRROR_URL="$MIRROR_URL"
EOF
}

load_data() {
    [[ -f "$DATA_FILE" ]] || return 1
    local line
    while IFS= read -r line; do
        case "$line" in
            SUB_URL=*)    SUB_URL="${line#SUB_URL=}"; SUB_URL="${SUB_URL%\"}"; SUB_URL="${SUB_URL#\"}" ;;
            MIRROR_URL=*) MIRROR_URL="${line#MIRROR_URL=}"; MIRROR_URL="${MIRROR_URL%\"}"; MIRROR_URL="${MIRROR_URL#\"}" ;;
        esac
    done < "$DATA_FILE"
    return 0
}

is_running() {
    [[ -f "$PID_FILE" ]] || return 1
    local pid
    pid=$(cat "$PID_FILE" 2>/dev/null) || return 1
    kill -0 "$pid" 2>/dev/null || { rm -f "$PID_FILE"; return 1; }
    # Verify it's actually mihomo, not a recycled PID
    if [[ -f "/proc/$pid/comm" ]]; then
        local comm
        comm=$(cat "/proc/$pid/comm" 2>/dev/null)
        [[ "$comm" == "mihomo" ]] || { rm -f "$PID_FILE"; return 1; }
    fi
    return 0
}

# All local API calls bypass proxy via --noproxy
api_get() {
    local path="$1"
    local header=""
    [[ -n "$API_SECRET" ]] && header="-H Authorization: Bearer $API_SECRET"
    curl -s --max-time 5 --noproxy '*' $header "$API_BASE$path" 2>/dev/null
}

api_put() {
    local path="$1"
    local data="$2"
    local header=""
    [[ -n "$API_SECRET" ]] && header="-H Authorization: Bearer $API_SECRET"
    curl -s --max-time 5 --noproxy '*' -X PUT $header \
        -H "Content-Type: application/json" \
        -d "$data" "$API_BASE$path" 2>/dev/null
}

check_deps() {
    for cmd in curl jq; do
        if ! command -v "$cmd" &>/dev/null; then
            err "Missing dependency: $cmd"
            echo "  Install: sudo apt install $cmd"
            exit 1
        fi
    done
}

# ---- Arrow-key selector ----

_is_separator() {
    [[ "$1" == ─* ]]
}

_next_selectable() {
    local pos="$1" dir="$2" count="$3"
    shift 3
    local items=("$@")
    while true; do
        ((pos += dir))
        if [[ $pos -lt 0 || $pos -ge $count ]]; then
            return 1
        fi
        if ! _is_separator "${items[$pos]}"; then
            echo "$pos"
            return 0
        fi
    done
}

arrow_select() {
    local title="$1"
    shift
    local current="$1"
    shift
    local items=("$@")
    local count=${#items[@]}

    [[ $count -eq 0 ]] && return 1
    [[ $current -ge $count ]] && current=0

    # Skip separator at initial position
    if _is_separator "${items[$current]}"; then
        current=$(_next_selectable "$current" 1 "$count" "${items[@]}") || current=0
    fi

    local page_size=15
    local offset=0
    local _drawn=0

    tput civis 2>/dev/null
    trap 'tput cnorm 2>/dev/null' INT TERM RETURN

    _draw_list() {
        if [[ $current -lt $offset ]]; then
            offset=$current
        elif [[ $current -ge $((offset + page_size)) ]]; then
            offset=$((current - page_size + 1))
        fi

        local end=$((offset + page_size))
        [[ $end -gt $count ]] && end=$count

        if [[ $_drawn -gt 0 ]]; then
            local lines_to_clear=$((_drawn + 2))
            for ((i = 0; i < lines_to_clear; i++)); do
                echo -ne "\033[A\033[2K"
            done
        fi

        echo -e "${CYAN}${title}${NC}  ${DIM}(↑↓/jk move  Enter confirm  q quit)${NC}"

        for ((i = offset; i < end; i++)); do
            if _is_separator "${items[$i]}"; then
                echo -e "  ${DIM}${items[$i]}${NC}"
            elif [[ $i -eq $current ]]; then
                echo -e "  ${REVERSE}${GREEN} → ${items[$i]} ${NC}"
            else
                echo -e "     ${items[$i]}"
            fi
        done

        if [[ $count -gt $page_size ]]; then
            echo -e "${DIM}    [$((current+1))/$count]${NC}"
        else
            echo ""
        fi

        _drawn=$((end - offset))
    }

    _draw_list

    while true; do
        IFS= read -rsn1 key
        case "$key" in
            $'\x1b')
                read -rsn2 -t 0.1 seq
                case "$seq" in
                    '[A')
                        local n
                        n=$(_next_selectable "$current" -1 "$count" "${items[@]}") && current=$n
                        ;;
                    '[B')
                        local n
                        n=$(_next_selectable "$current" 1 "$count" "${items[@]}") && current=$n
                        ;;
                    '[5') read -rsn1 -t 0.1; ((current -= page_size)); ((current < 0)) && current=0
                          if _is_separator "${items[$current]}"; then
                              current=$(_next_selectable "$current" 1 "$count" "${items[@]}") || true
                          fi ;;
                    '[6') read -rsn1 -t 0.1; ((current += page_size)); ((current >= count)) && current=$((count - 1))
                          if _is_separator "${items[$current]}"; then
                              current=$(_next_selectable "$current" -1 "$count" "${items[@]}") || true
                          fi ;;
                esac
                ;;
            '') tput cnorm 2>/dev/null; ARROW_RESULT=$current; return 0 ;;
            'q'|'Q') tput cnorm 2>/dev/null; ARROW_RESULT=-1; return 1 ;;
            'k')
                local n
                n=$(_next_selectable "$current" -1 "$count" "${items[@]}") && current=$n
                ;;
            'j')
                local n
                n=$(_next_selectable "$current" 1 "$count" "${items[@]}") && current=$n
                ;;
        esac
        _draw_list
    done
}

# ---- Install mihomo ----

install_mihomo() {
    mkdir -p "$MIHOMO_DIR"
    load_data 2>/dev/null || true

    echo ""
    echo -e "${CYAN}=== Install Mihomo ===${NC}"
    info "Install dir : $MIHOMO_DIR"
    info "Binary      : $MIHOMO_BIN"
    info "Config      : $CONFIG_FILE"
    info "Log         : $LOG_FILE"
    info "Data        : $DATA_FILE"
    echo ""

    if [[ -f "$MIHOMO_BIN" ]]; then
        local ver
        ver=$("$MIHOMO_BIN" -v 2>/dev/null | head -1 || echo "unknown")
        msg "Mihomo already installed"
        info "Path: $MIHOMO_BIN"
        info "Version: $ver"
        echo ""
        read -rp "Reinstall / upgrade? [y/N]: " choice
        [[ "$choice" != "y" && "$choice" != "Y" ]] && return
    fi

    # Detect arch
    local arch
    case "$(uname -m)" in
        x86_64)  arch="amd64" ;;
        aarch64) arch="arm64" ;;
        armv7l)  arch="armv7" ;;
        *)
            err "Unsupported arch: $(uname -m)"
            return 1
            ;;
    esac
    msg "Arch: $arch"

    # Choose download source
    echo ""
    echo "  Download source:"
    echo "    1) GitHub official (default)"
    echo "    2) Custom mirror"
    echo ""
    read -rp "Choose [1-2, default 1]: " src_choice

    if [[ "$src_choice" == "2" ]]; then
        echo ""
        if [[ -n "$MIRROR_URL" ]]; then
            echo -e "  Saved mirror: ${YELLOW}$MIRROR_URL${NC}"
            read -rp "  Enter mirror URL (Enter to keep current): " input_mirror
            [[ -n "$input_mirror" ]] && MIRROR_URL="$input_mirror"
        else
            echo -e "  ${DIM}The mirror should serve the mihomo binary directly.${NC}"
            echo -e "  ${DIM}The script will request: <mirror_url>/mihomo-linux-<arch>${NC}"
            echo ""
            read -rp "  Enter mirror base URL: " MIRROR_URL
        fi

        if [[ -z "$MIRROR_URL" ]]; then
            err "Mirror URL cannot be empty"
            return 1
        fi

        # Remove trailing slash
        MIRROR_URL="${MIRROR_URL%/}"
        save_data

        local url="${MIRROR_URL}/mihomo-linux-${arch}"
        msg "Downloading from mirror: $url"

        if ! curl -L --max-time 120 --progress-bar -o "$MIHOMO_BIN" "$url"; then
            err "Download failed"
            return 1
        fi

        chmod +x "$MIHOMO_BIN"

    else
        # GitHub official
        msg "Fetching latest version from GitHub..."

        local latest_tag
        latest_tag=$(curl -sL --max-time 10 \
            "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" \
            | jq -r '.tag_name' 2>/dev/null)

        if [[ -z "$latest_tag" || "$latest_tag" == "null" ]]; then
            err "Cannot fetch latest version. Network issue or GitHub blocked?"
            warn "Try option 2 (custom mirror) instead"
            return 1
        fi

        msg "Latest version: $latest_tag"

        local filename="mihomo-linux-${arch}-${latest_tag}.gz"
        local url="https://github.com/MetaCubeX/mihomo/releases/download/${latest_tag}/${filename}"

        msg "Downloading: $url"
        if ! curl -L --max-time 120 --progress-bar -o "/tmp/$filename" "$url"; then
            err "Download failed"
            return 1
        fi

        msg "Extracting to $MIHOMO_BIN ..."
        gunzip -f "/tmp/$filename"
        mv "/tmp/mihomo-linux-${arch}-${latest_tag}" "$MIHOMO_BIN"
        chmod +x "$MIHOMO_BIN"
    fi

    if [[ -x "$MIHOMO_BIN" ]]; then
        msg "Install successful"
        info "Path: $MIHOMO_BIN"
        info "Version: $("$MIHOMO_BIN" -v 2>/dev/null | head -1)"
    else
        err "Install failed"
    fi
}

# ---- Subscription ----

set_subscription() {
    load_data 2>/dev/null || true

    echo ""
    echo -e "${CYAN}=== Subscription ===${NC}"
    info "Stored at: $DATA_FILE"
    echo ""

    if [[ -n "$SUB_URL" ]]; then
        echo -e "  Current: ${YELLOW}$SUB_URL${NC}"
        echo ""
        read -rp "Enter new subscription URL (Enter to keep current): " input
        [[ -n "$input" ]] && SUB_URL="$input"
    else
        read -rp "Enter subscription URL: " SUB_URL
    fi

    if [[ -z "$SUB_URL" ]]; then
        err "Subscription URL cannot be empty"
        return 1
    fi

    save_data
    msg "Subscription URL saved"
    info "Stored at: $DATA_FILE"
}

update_subscription() {
    load_data 2>/dev/null || true

    if [[ -z "$SUB_URL" ]]; then
        err "No subscription URL set"
        set_subscription || return
    fi

    echo ""
    echo -e "${CYAN}=== Update Subscription ===${NC}"
    info "URL:  $SUB_URL"
    info "Save: $CONFIG_FILE"
    echo ""

    msg "Downloading config..."
    local tmp="/tmp/clash-sub-$$.yaml"

    if ! curl -sL --max-time 30 -o "$tmp" "$SUB_URL"; then
        err "Download failed, check URL"
        return 1
    fi

    if ! head -20 "$tmp" | grep -qE '(proxies:|proxy-providers:|port:|mixed-port:)'; then
        err "Downloaded file is not a valid Clash config"
        rm -f "$tmp"
        return 1
    fi

    ensure_api_config "$tmp"
    mv "$tmp" "$CONFIG_FILE"

    local node_count
    node_count=$(grep -c '^\s*- {name:\|^\s*- name:' "$CONFIG_FILE" 2>/dev/null || echo '0')

    msg "Config updated"
    info "File:  $CONFIG_FILE"
    info "Size:  $(du -h "$CONFIG_FILE" | cut -f1)"
    info "Nodes: $node_count"

    if is_running; then
        echo ""
        msg "Mihomo is running, reloading config..."
        api_put "/configs" '{"path":"'"$CONFIG_FILE"'"}'
        msg "Config reloaded"
    fi
}

ensure_api_config() {
    local file="$1"
    if ! grep -q 'external-controller' "$file"; then
        sed -i "1i external-controller: 127.0.0.1:$API_PORT" "$file"
    fi
    if ! grep -qE '(mixed-port|port):' "$file"; then
        sed -i "1i mixed-port: 7890" "$file"
    fi
    if ! grep -q '^mode:' "$file"; then
        sed -i "1i mode: rule" "$file"
    fi
}

# ---- Node list ----

do_self_update() {
    local dl_url="$1"
    local self_path
    self_path="$(readlink -f "$0")"
    msg "Downloading from: $dl_url"
    if curl -fsSL --max-time 30 -o "${self_path}.tmp" "$dl_url"; then
        # Validate it's a real bash script
        local first_line
        first_line=$(head -1 "${self_path}.tmp")
        if [[ "$first_line" != "#!/bin/bash" && "$first_line" != "#!/usr/bin/env bash" ]]; then
            err "Downloaded file is not a valid bash script (bad shebang: $first_line)"
            rm -f "${self_path}.tmp"
            return 1
        fi
        if ! bash -n "${self_path}.tmp" 2>/dev/null; then
            err "Downloaded script has syntax errors"
            rm -f "${self_path}.tmp"
            return 1
        fi
        chmod +x "${self_path}.tmp"
        mv "${self_path}.tmp" "$self_path"
        msg "Updated successfully: $self_path"
        if [[ -f /usr/local/bin/clash ]]; then
            sudo cp "$self_path" /usr/local/bin/clash
            sudo chmod +x /usr/local/bin/clash
            msg "Also updated /usr/local/bin/clash"
        fi
    else
        rm -f "${self_path}.tmp"
        err "Download failed"
        return 1
    fi
}

_format_delay() {
    local delay="$1"
    if [[ "$delay" -gt 0 ]]; then
        if [[ "$delay" -lt 200 ]]; then
            echo -e "${GREEN}${delay}ms${NC}"
        elif [[ "$delay" -lt 500 ]]; then
            echo -e "${YELLOW}${delay}ms${NC}"
        else
            echo -e "${RED}${delay}ms${NC}"
        fi
    else
        echo -e "${DIM}--${NC}"
    fi
}

list_nodes() {
    if ! is_running; then
        if [[ ! -f "$CONFIG_FILE" ]]; then
            err "Config not found, update subscription first"
            return
        fi
        echo ""
        echo -e "${CYAN}=== Node List (offline) ===${NC}"
        info "Config: $CONFIG_FILE"
        echo ""
        local i=1
        grep -oP '(?<=name:\s)[^,}]+|(?<=name: )[^,}]+' "$CONFIG_FILE" \
            | while IFS= read -r name; do
                name=$(echo "$name" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
                printf "  ${YELLOW}%3d${NC}) %s\n" "$i" "$name"
                ((i++))
            done
        echo ""
        warn "Mihomo not running, no latency info"
        return
    fi

    local pid
    pid=$(cat "$PID_FILE" 2>/dev/null)

    echo ""
    echo -e "${CYAN}=== Node List ===${NC}"
    info "Process: mihomo (PID $pid)"
    info "API:     $API_BASE"
    echo ""

    # Fetch all proxies in one request
    local all_proxies
    all_proxies=$(api_get "/proxies")

    local groups
    groups=$(echo "$all_proxies" | jq -r '.proxies | to_entries[] | select(.value.type == "Selector" or .value.type == "URLTest" or .value.type == "Fallback" or .value.type == "LoadBalance") | .key' 2>/dev/null)

    if [[ -z "$groups" ]]; then
        err "Cannot fetch proxy groups (API may not be ready)"
        return
    fi

    while IFS= read -r group; do
        local group_type now
        group_type=$(echo "$all_proxies" | jq -r --arg k "$group" '.proxies[$k].type' 2>/dev/null)
        now=$(echo "$all_proxies" | jq -r --arg k "$group" '.proxies[$k].now // empty' 2>/dev/null)

        echo -e "  ${CYAN}${BOLD}[$group]${NC} ${DIM}($group_type)${NC}  active: ${GREEN}$now${NC}"

        local nodes
        nodes=$(echo "$all_proxies" | jq -r --arg k "$group" '.proxies[$k].all[]? // empty' 2>/dev/null)
        if [[ -n "$nodes" ]]; then
            local idx=1
            while IFS= read -r node; do
                local marker="  "
                [[ "$node" == "$now" ]] && marker="${GREEN}→ ${NC}"

                local delay
                delay=$(echo "$all_proxies" | jq -r --arg k "$node" '.proxies[$k].history[-1].delay // 0' 2>/dev/null)
                local delay_str
                delay_str=$(_format_delay "$delay")

                printf "    ${marker}${YELLOW}%2d${NC}) %-40s %b\n" "$idx" "$node" "$delay_str"
                ((idx++))
            done <<< "$nodes"
        fi
        echo ""
    done <<< "$groups"
}

# ---- Switch node ----

switch_node() {
    if ! is_running; then
        err "Mihomo not running, start it first"
        return
    fi

    local pid
    pid=$(cat "$PID_FILE" 2>/dev/null)

    echo ""
    echo -e "${CYAN}=== Switch Node ===${NC}"
    info "Process: mihomo (PID $pid)"
    info "API:     $API_BASE"
    echo ""

    local groups_json
    groups_json=$(api_get "/proxies")
    local groups
    groups=$(echo "$groups_json" | jq -r '.proxies | to_entries[] | select(.value.type == "Selector") | .key' 2>/dev/null)

    if [[ -z "$groups" ]]; then
        err "No Selector proxy groups found"
        return
    fi

    local group_arr=()
    local group_display=()
    while IFS= read -r g; do
        local now
        now=$(echo "$groups_json" | jq -r --arg k "$g" '.proxies[$k].now // "none"' 2>/dev/null)
        group_arr+=("$g")
        group_display+=("$(printf '%-25s %b' "$g" "${DIM}active: $now${NC}")")
    done <<< "$groups"

    local selected_group
    if [[ ${#group_arr[@]} -eq 1 ]]; then
        selected_group="${group_arr[0]}"
        msg "Proxy group: $selected_group"
    else
        arrow_select "Select proxy group:" 0 "${group_display[@]}" || return
        selected_group="${group_arr[$ARROW_RESULT]}"
        echo ""
        msg "Proxy group: $selected_group"
    fi

    local group_url
    group_url=$(jq -rn --arg s "$selected_group" '$s|@uri')

    local now
    now=$(echo "$groups_json" | jq -r --arg k "$selected_group" '.proxies[$k].now // empty')

    local node_arr=()
    local node_display=()
    local current_idx=0
    local idx=0

    while IFS= read -r node; do
        node_arr+=("$node")

        local delay
        delay=$(echo "$groups_json" | jq -r --arg k "$node" '.proxies[$k].history[-1].delay // 0' 2>/dev/null)
        local delay_str
        delay_str=$(_format_delay "$delay")

        local current_mark=""
        if [[ "$node" == "$now" ]]; then
            current_mark="  ${GREEN}◀ active${NC}"
            current_idx=$idx
        fi

        node_display+=("$(printf '%-38s %b%b' "$node" "$delay_str" "$current_mark")")
        ((idx++))
    done < <(echo "$groups_json" | jq -r --arg k "$selected_group" '.proxies[$k].all[]? // empty')

    if [[ ${#node_arr[@]} -eq 0 ]]; then
        err "No nodes in this proxy group"
        return
    fi

    echo ""
    arrow_select "Select node [$selected_group]:" "$current_idx" "${node_display[@]}" || return

    local target="${node_arr[$ARROW_RESULT]}"

    api_put "/proxies/$group_url" "{\"name\":\"$target\"}" > /dev/null

    local new_now
    new_now=$(api_get "/proxies/$group_url" | jq -r '.now // empty')

    echo ""
    if [[ "$new_now" == "$target" ]]; then
        msg "Switched: ${YELLOW}$now${NC} → ${GREEN}$target${NC}"
    else
        err "Switch failed"
    fi
}

# ---- Speed test ----

test_delay() {
    if ! is_running; then
        err "Mihomo not running"
        return
    fi

    echo ""
    echo -e "${CYAN}=== Speed Test ===${NC}"
    echo ""

    local groups
    groups=$(api_get "/proxies" | jq -r '.proxies | to_entries[] | select(.value.type == "Selector" or .value.type == "URLTest") | .key' 2>/dev/null)

    local group_arr=()
    while IFS= read -r g; do
        group_arr+=("$g")
    done <<< "$groups"

    local group
    if [[ ${#group_arr[@]} -eq 1 ]]; then
        group="${group_arr[0]}"
    else
        arrow_select "Select proxy group to test:" 0 "${group_arr[@]}" || return
        group="${group_arr[$ARROW_RESULT]}"
    fi

    echo ""
    msg "Testing [$group], please wait..."

    local group_url
    group_url=$(jq -rn --arg s "$group" '$s|@uri')

    local result
    result=$(curl -s --max-time 30 --noproxy '*' -X GET \
        "$API_BASE/group/$group_url/delay?url=https://www.gstatic.com/generate_204&timeout=3000" 2>/dev/null)

    echo ""
    echo "$result" | jq -r 'to_entries | sort_by(.value) | .[] | "\(.key)\t\(.value)"' 2>/dev/null \
        | while IFS=$'\t' read -r name delay; do
            if [[ "$delay" -gt 0 && "$delay" -lt 5000 ]]; then
                local color="$GREEN"
                [[ "$delay" -ge 200 ]] && color="$YELLOW"
                [[ "$delay" -ge 500 ]] && color="$RED"
                printf "  %-40s ${color}%dms${NC}\n" "$name" "$delay"
            else
                printf "  %-40s ${RED}timeout${NC}\n" "$name"
            fi
        done
    echo ""
}

# ---- Start / Stop ----

start_service() {
    if is_running; then
        local pid
        pid=$(cat "$PID_FILE")
        warn "Mihomo already running"
        info "Process: mihomo (PID $pid)"
        info "Binary:  $MIHOMO_BIN"
        info "Config:  $CONFIG_FILE"
        return
    fi

    if [[ ! -f "$CONFIG_FILE" ]]; then
        err "Config not found"
        info "Expected: $CONFIG_FILE"
        info "Run subscription update first"
        return
    fi

    if [[ ! -f "$MIHOMO_BIN" ]]; then
        err "Mihomo not installed"
        info "Expected: $MIHOMO_BIN"
        return
    fi

    echo ""
    echo -e "${CYAN}=== Start Mihomo ===${NC}"
    info "Binary : $MIHOMO_BIN"
    info "Version: $("$MIHOMO_BIN" -v 2>/dev/null | head -1)"
    info "Workdir: $MIHOMO_DIR"
    info "Config : $CONFIG_FILE"
    info "Log    : $LOG_FILE"
    info "PID    : $PID_FILE"
    echo ""

    # Rotate log if larger than 10MB
    if [[ -f "$LOG_FILE" ]]; then
        local log_size
        log_size=$(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0)
        if [[ "$log_size" -gt 10485760 ]]; then
            mv "$LOG_FILE" "${LOG_FILE}.old"
            msg "Log rotated (was $(( log_size / 1024 / 1024 ))MB)"
        fi
    fi

    msg "Starting Mihomo..."
    nohup "$MIHOMO_BIN" -d "$MIHOMO_DIR" -f "$CONFIG_FILE" > "$LOG_FILE" 2>&1 &
    echo $! > "$PID_FILE"
    sleep 2

    if is_running; then
        local pid
        pid=$(cat "$PID_FILE")

        local mixed_port
        mixed_port=$(grep -oP '(?<=mixed-port:\s)\d+' "$CONFIG_FILE" 2>/dev/null || echo "7890")

        msg "Started successfully"
        echo ""
        echo -e "  ${CYAN}Process${NC}"
        info "Binary: $MIHOMO_BIN"
        info "PID:    $pid"
        info "API:    $API_BASE"
        echo ""
        echo -e "  ${CYAN}Proxy${NC}"
        info "HTTP/SOCKS5: 127.0.0.1:$mixed_port"
        echo ""
        echo -e "  ${CYAN}Terminal proxy${NC}"
        echo -e "    ${GREEN}Enable :${NC} eval \$($0 proxy-on)"
        echo -e "    ${RED}Disable:${NC} eval \$($0 proxy-off)"
    else
        err "Failed to start"
        info "Check log: tail -30 $LOG_FILE"
        echo ""
        tail -20 "$LOG_FILE"
    fi
}

stop_service() {
    if ! is_running; then
        warn "Mihomo not running"
        return
    fi

    local pid
    pid=$(cat "$PID_FILE")
    kill "$pid" 2>/dev/null
    rm -f "$PID_FILE"
    msg "Mihomo stopped"
    info "Terminated PID $pid"
}

restart_service() {
    echo ""
    stop_service 2>/dev/null
    sleep 1
    start_service
}

show_status() {
    echo ""
    echo -e "${CYAN}=== Status ===${NC}"
    echo ""

    if is_running; then
        local pid
        pid=$(cat "$PID_FILE")
        msg "${GREEN}●${NC} Mihomo running"
        info "Binary: $MIHOMO_BIN"
        info "PID:    $pid"

        local proc_info
        proc_info=$(ps -p "$pid" -o pid,vsz,rss,etime,command --no-headers 2>/dev/null)
        if [[ -n "$proc_info" ]]; then
            local mem rss uptime
            mem=$(echo "$proc_info" | awk '{printf "%.1f MB", $2/1024}')
            rss=$(echo "$proc_info" | awk '{printf "%.1f MB", $3/1024}')
            uptime=$(echo "$proc_info" | awk '{print $4}')
            info "Mem (VSZ): $mem"
            info "Mem (RSS): $rss"
            info "Uptime:    $uptime"
        fi
        info "API: $API_BASE"
    else
        warn "${RED}●${NC} Mihomo not running"
    fi

    echo ""
    echo -e "${CYAN}=== Files ===${NC}"
    echo ""

    if [[ -f "$MIHOMO_BIN" ]]; then
        msg "Binary: $MIHOMO_BIN"
        info "Version: $("$MIHOMO_BIN" -v 2>/dev/null | head -1)"
        info "Size:    $(du -h "$MIHOMO_BIN" | cut -f1)"
    else
        warn "Binary: $MIHOMO_BIN (not found)"
    fi

    if [[ -f "$CONFIG_FILE" ]]; then
        local node_count
        node_count=$(grep -c '^\s*- {name:\|^\s*- name:' "$CONFIG_FILE" 2>/dev/null || echo '0')
        msg "Config: $CONFIG_FILE"
        info "Size:  $(du -h "$CONFIG_FILE" | cut -f1)"
        info "Nodes: $node_count"
    else
        warn "Config: $CONFIG_FILE (not found)"
    fi

    load_data 2>/dev/null || true
    if [[ -n "$SUB_URL" ]]; then
        msg "Subscription: $SUB_URL"
        info "Stored at: $DATA_FILE"
    else
        warn "Subscription: not set"
    fi

    if [[ -n "$MIRROR_URL" ]]; then
        msg "Mirror: $MIRROR_URL"
    fi

    if [[ -f "$LOG_FILE" ]]; then
        msg "Log: $LOG_FILE"
        info "Size: $(du -h "$LOG_FILE" | cut -f1)"
    fi

    if [[ -f "$CONFIG_FILE" ]]; then
        local mixed_port
        mixed_port=$(grep -oP '(?<=mixed-port:\s)\d+' "$CONFIG_FILE" 2>/dev/null || echo "7890")
        echo ""
        echo -e "${CYAN}=== Proxy ===${NC}"
        echo ""
        info "HTTP/SOCKS5: 127.0.0.1:$mixed_port"

        if [[ -n "${https_proxy:-}" ]]; then
            msg "Terminal proxy: enabled ($https_proxy)"
        else
            warn "Terminal proxy: disabled"
            info "Run: eval \$($0 proxy-on)"
        fi
    fi
    echo ""
}

show_logs() {
    if [[ -f "$LOG_FILE" ]]; then
        echo ""
        echo -e "${CYAN}=== Logs ===${NC}"
        info "File: $LOG_FILE"
        info "Size: $(du -h "$LOG_FILE" | cut -f1)"
        echo ""
        tail -30 "$LOG_FILE"
        echo ""
        info "Live: tail -f $LOG_FILE"
    else
        warn "No log file yet"
        info "Expected: $LOG_FILE"
    fi
}

# ---- Proxy toggle ----

proxy_on() {
    local mixed_port
    mixed_port=$(grep -oP '(?<=mixed-port:\s)\d+' "$CONFIG_FILE" 2>/dev/null || echo "7890")
    echo "export https_proxy=http://127.0.0.1:$mixed_port"
    echo "export http_proxy=http://127.0.0.1:$mixed_port"
    echo "export all_proxy=socks5://127.0.0.1:$mixed_port"
}

proxy_off() {
    echo "unset https_proxy http_proxy all_proxy"
}

# ---- Quick setup ----

quick_setup() {
    echo ""
    echo -e "${CYAN}=== Quick Setup ===${NC}"
    info "This will guide you through the full setup process."
    echo ""

    # Step 1: Install
    if [[ ! -f "$MIHOMO_BIN" ]]; then
        msg "Step 1/5: Install Mihomo"
        install_mihomo || { err "Install failed, aborting"; return 1; }
    else
        local ver
        ver=$("$MIHOMO_BIN" -v 2>/dev/null | head -1 || echo "unknown")
        msg "Step 1/5: Mihomo already installed ($ver)"
    fi
    echo ""

    # Step 2: Subscription
    load_data 2>/dev/null || true
    if [[ -z "$SUB_URL" ]]; then
        msg "Step 2/5: Set subscription URL"
        set_subscription || { err "Subscription setup failed, aborting"; return 1; }
    else
        msg "Step 2/5: Subscription already set"
        info "URL: $SUB_URL"
    fi
    echo ""

    # Step 3: Update config
    msg "Step 3/5: Updating subscription config..."
    update_subscription || { err "Config update failed, aborting"; return 1; }
    echo ""

    # Step 4: Start service
    msg "Step 4/5: Starting Mihomo..."
    if is_running; then
        restart_service
    else
        start_service
    fi
    echo ""

    # Step 5: Proxy hint
    msg "Step 5/5: Enable terminal proxy"
    echo ""
    echo -e "  ${YELLOW}Run this to enable proxy in current shell:${NC}"
    echo ""
    echo "    eval \$($0 proxy-on)"
    echo ""
    info "Or install the shell helper via: $0 (menu → Proxy → Install shell helper)"
    echo ""
    msg "Setup complete!"
}

# ---- Shell helper installer ----

install_shell_helper() {
    local clash_bin
    if [[ -f /usr/local/bin/clash ]]; then
        clash_bin="/usr/local/bin/clash"
    else
        clash_bin="$(readlink -f "$0")"
    fi

    local snippet
    snippet=$(cat << 'FUNC'
# Clash proxy helper
clashproxy() {
    case "${1:-}" in
        on)  eval $(__CLASH_BIN__ proxy-on); echo "Proxy enabled: $https_proxy" ;;
        off) eval $(__CLASH_BIN__ proxy-off); echo "Proxy disabled" ;;
        *)   echo "Usage: clashproxy on|off" ;;
    esac
}
FUNC
)
    snippet="${snippet//__CLASH_BIN__/$clash_bin}"

    # Detect shell rc file
    local rc_file
    if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$SHELL" == */zsh ]]; then
        rc_file="$HOME/.zshrc"
    else
        rc_file="$HOME/.bashrc"
    fi

    if grep -q 'clashproxy()' "$rc_file" 2>/dev/null; then
        warn "clashproxy function already exists in $rc_file"
        read -rp "Overwrite? [y/N]: " choice
        if [[ "$choice" == "y" || "$choice" == "Y" ]]; then
            # Remove old function block
            sed -i '/# Clash proxy helper/,/^}/d' "$rc_file"
        else
            return
        fi
    fi

    echo "" >> "$rc_file"
    echo "$snippet" >> "$rc_file"
    msg "Added clashproxy function to $rc_file"
    echo ""
    info "Restart your shell or run: source $rc_file"
    info "Then use:"
    info "  clashproxy on    - enable proxy"
    info "  clashproxy off   - disable proxy"
}

# ---- Main menu ----

menu_service() {
    local items=(
        "Start"
        "Stop"
        "Restart"
        "Status"
        "View logs"
    )
    echo ""
    arrow_select "Service:" 0 "${items[@]}" || return
    case $ARROW_RESULT in
        0) start_service ;;
        1) stop_service ;;
        2) restart_service ;;
        3) show_status ;;
        4) show_logs ;;
    esac
}

menu_nodes() {
    local items=(
        "List nodes"
        "Switch node"
        "Speed test"
    )
    echo ""
    arrow_select "Nodes:" 0 "${items[@]}" || return
    case $ARROW_RESULT in
        0) list_nodes ;;
        1) switch_node ;;
        2) test_delay ;;
    esac
}

menu_proxy() {
    local items=(
        "Enable terminal proxy"
        "Disable terminal proxy"
        "Install shell helper (clashproxy on/off)"
    )
    echo ""
    arrow_select "Proxy:" 0 "${items[@]}" || return
    case $ARROW_RESULT in
        0)
            echo ""
            echo -e "${YELLOW}Run this command in your terminal:${NC}"
            echo ""
            echo "  eval \$($0 proxy-on)"
            echo ""
            info "This sets http_proxy, https_proxy, all_proxy for the current shell."
            ;;
        1)
            echo ""
            echo -e "${YELLOW}Run this command in your terminal:${NC}"
            echo ""
            echo "  eval \$($0 proxy-off)"
            echo ""
            info "This unsets http_proxy, https_proxy, all_proxy."
            ;;
        2)
            install_shell_helper
            ;;
    esac
}

menu_tools() {
    local items=(
        "Quick Setup (one-click)"
        "Install clash to PATH"
        "Self-update clash.sh"
        "Remove all (uninstall)"
    )
    echo ""
    arrow_select "Tools:" 0 "${items[@]}" || return
    case $ARROW_RESULT in
        0) quick_setup ;;
        1)
            local target="/usr/local/bin/clash"
            msg "Installing clash to $target ..."
            sudo cp "$(readlink -f "$0")" "$target"
            sudo chmod +x "$target"
            msg "Done! You can now run 'clash' from anywhere."
            ;;
        2)
            read -rp "Enter the full URL to download new clash.sh: " dl_url
            if [[ -z "$dl_url" ]]; then
                err "URL cannot be empty"
            else
                do_self_update "$dl_url"
            fi
            ;;
        3)
            echo ""
            warn "This will PERMANENTLY remove ALL mihomo/clash files:"
            info "- Stop running mihomo process"
            info "- $MIHOMO_DIR (binary, config, logs, data)"
            info "- /usr/local/bin/clash (if exists)"
            echo ""
            read -rp "Type 'remove' to confirm: " confirm1
            if [[ "$confirm1" != "remove" ]]; then
                info "Cancelled"
                return
            fi
            read -rp "Are you REALLY sure? This cannot be undone. [y/N]: " confirm2
            if [[ "$confirm2" != "y" && "$confirm2" != "Y" ]]; then
                info "Cancelled"
                return
            fi
            is_running && stop_service
            rm -rf "$MIHOMO_DIR"
            msg "Removed $MIHOMO_DIR"
            if [[ -f /usr/local/bin/clash ]]; then
                sudo rm -f /usr/local/bin/clash
                msg "Removed /usr/local/bin/clash"
            fi
            msg "All cleaned up"
            ;;
    esac
}

main_menu() {
    check_deps
    print_banner

    if is_running; then
        local pid
        pid=$(cat "$PID_FILE" 2>/dev/null)
        echo -e "  Status: ${GREEN}●${NC} Running  ${DIM}PID $pid${NC}  ${DIM}$MIHOMO_BIN${NC}"
    else
        echo -e "  Status: ${RED}●${NC} Stopped"
    fi
    if [[ -f "$CONFIG_FILE" ]]; then
        echo -e "  Config: ${DIM}$CONFIG_FILE${NC}"
    fi

    # Show quick setup hint for first-time users
    if [[ ! -f "$MIHOMO_BIN" ]] || [[ ! -f "$CONFIG_FILE" ]]; then
        echo ""
        echo -e "  ${YELLOW}★ First time? Go to ${BOLD}Tools ▸ Quick Setup${NC}${YELLOW} for one-click setup${NC}"
    fi
    echo ""

    local menu_items=(
        "Install / Update Mihomo"
        "Set subscription URL"
        "Update subscription"
        "───────────────────────"
        "Service  ▸"
        "Nodes    ▸"
        "Proxy    ▸"
        "───────────────────────"
        "Tools    ▸"
        "───────────────────────"
        "Exit"
    )

    local last_pos=0
    while true; do
        arrow_select "Main Menu:" "$last_pos" "${menu_items[@]}" || { echo ""; msg "Bye"; exit 0; }
        last_pos=$ARROW_RESULT

        case $ARROW_RESULT in
            0) install_mihomo ;;
            1) set_subscription ;;
            2) update_subscription ;;
            4) menu_service ;;
            5) menu_nodes ;;
            6) menu_proxy ;;
            8) menu_tools ;;
            10) echo ""; msg "Bye"; exit 0 ;;
        esac
        echo ""
    done
}

# ---- CLI shortcuts ----

case "${1:-}" in
    setup)      check_deps; quick_setup ;;
    install)    check_deps; install_mihomo ;;
    install-to-path)
        local target="/usr/local/bin/clash"
        msg "Installing clash to $target ..."
        sudo cp "$(readlink -f "$0")" "$target"
        sudo chmod +x "$target"
        msg "Done! You can now run 'clash' from anywhere."
        ;;
    self-update)
        if [[ -z "${2:-}" ]]; then
            read -rp "Enter the full URL to download new clash.sh: " dl_url
        else
            dl_url="$2"
        fi
        if [[ -z "$dl_url" ]]; then
            err "URL cannot be empty"
            exit 1
        fi
        do_self_update "$dl_url" || exit 1
        ;;
    sub)        check_deps; set_subscription ;;
    update)     check_deps; update_subscription ;;
    start)      check_deps; start_service ;;
    stop)       stop_service ;;
    restart)    check_deps; restart_service ;;
    status)     show_status ;;
    nodes|list) check_deps; list_nodes ;;
    switch)     check_deps; switch_node ;;
    test)       check_deps; test_delay ;;
    logs)       show_logs ;;
    proxy-on)   proxy_on ;;
    proxy-off)  proxy_off ;;
    remove|uninstall)
        warn "This will PERMANENTLY remove ALL mihomo/clash files:"
        info "- Stop running mihomo process"
        info "- $MIHOMO_DIR (binary, config, logs, data)"
        info "- /usr/local/bin/clash (if exists)"
        echo ""
        read -rp "Type 'remove' to confirm: " confirm1
        if [[ "$confirm1" != "remove" ]]; then
            info "Cancelled"
            exit 0
        fi
        read -rp "Are you REALLY sure? This cannot be undone. [y/N]: " confirm2
        if [[ "$confirm2" != "y" && "$confirm2" != "Y" ]]; then
            info "Cancelled"
            exit 0
        fi
        is_running && stop_service
        rm -rf "$MIHOMO_DIR"
        msg "Removed $MIHOMO_DIR"
        if [[ -f /usr/local/bin/clash ]]; then
            sudo rm -f /usr/local/bin/clash
            msg "Removed /usr/local/bin/clash"
        fi
        msg "All cleaned up"
        ;;
    help|-h|--help)
        echo "Usage: $0 [command]"
        echo ""
        echo "Setup:"
        echo "  setup       One-click guided setup"
        echo "  install     Install / update Mihomo"
        echo "  sub         Set subscription URL"
        echo "  update      Download / update subscription"
        echo ""
        echo "Service:"
        echo "  start       Start Mihomo"
        echo "  stop        Stop Mihomo"
        echo "  restart     Restart Mihomo"
        echo "  status      Show status (process / files / proxy)"
        echo ""
        echo "Nodes:"
        echo "  nodes       List nodes with latency"
        echo "  switch      Arrow-key node switching"
        echo "  test        Speed test"
        echo ""
        echo "Proxy:"
        echo "  eval \$($0 proxy-on)    Enable terminal proxy"
        echo "  eval \$($0 proxy-off)   Disable terminal proxy"
        echo ""
        echo "Other:"
        echo "  install-to-path  Install this script to /usr/local/bin/clash"
        echo "  self-update [url]  Update clash.sh from a remote URL"
        echo "  remove      Remove all mihomo/clash files"
        echo "  logs        View logs"
        echo ""
        echo "Files:"
        echo "  Binary  $MIHOMO_BIN"
        echo "  Config  $CONFIG_FILE"
        echo "  Log     $LOG_FILE"
        echo "  Data    $DATA_FILE"
        echo ""
        echo "Run without arguments for interactive menu"
        ;;
    "")
        main_menu
        ;;
    *)
        err "Unknown command: $1"
        echo "Run $0 help for usage"
        exit 1
        ;;
esac