<script setup>
import { ref, computed, onMounted, onUnmounted } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import {
  connectDeviceWifiAPI,
  getDeviceBatteryAPI,
  getHardwareBoxTaskStatusAPI,
  getHardwareBoxVersionAPI,
  sendHardwareBoxUpdateAPI,
} from "@/apis";

const STATES = {
  SCANNING: "SCANNING",
  LIST: "LIST",
  CONNECTING: "CONNECTING",
  CONNECTED: "CONNECTED",
  UPDATING: "UPDATING",
  DONE: "DONE",
  FAILED: "FAILED",
};

const isIOS = uni.getDeviceInfo().osName === "ios";
const currentState = ref(STATES.SCANNING);
const connectedWifi = ref(null);
const wifiList = ref([]);
const showWifiBanner = ref(false);

const connectingWifi = ref(null);
const connectInput = ref({ ssid: "", password: "" });
const connectMode = ref("secure"); // secure | open | manual
const connectError = ref("");
const connectStatusText = ref("");
const isSubmittingWifi = ref(false);
const keyboardHeight = ref(0);
// 控制密码输入框是否显示明文
const showPassword = ref(false);
// 刷新防抖标志：扫描进行中为 true，禁止重复点击；扫描结束（成功/失败）后重置为 false。
const isRefreshing = ref(false);
const isStartingUpdate = ref(false);
const routeOtaInfo = ref({
  versionNumber: "",
  resourceUrl: "",
});

const progress = ref(0);
let progressTimer = null;
let timeoutTimer = null;
let statusTimer = null;
let wifiConnectTimer = null;
let wifiConnectRequestId = 0;
let wifiConnectPollCount = 0;
const WIFI_CONNECT_POLL_INTERVAL = 2000;
const WIFI_CONNECT_MAX_POLL_COUNT = 30;
const WIFI_CONNECT_FAILED_TEXT = "连接失败，请检查WiFi密码或WiFi状态";
const OTA_MIN_BATTERY = 20;
const OTA_LOW_BATTERY_TEXT = "电量不足 50%，暂不支持 OTA 升级";
// 控制授权拒绝弹窗显示/隐藏
const wifiAuthDeniedVisible = ref(false);

// 判断 WiFi 列表失败是否由用户拒绝授权引起（兼容 errno:103 及各平台 errMsg 变体）。
const isWifiPermissionDenied = (err) => {
  if (err?.errno === 103) return true;
  const errMsg = err?.errMsg || "";
  return /auth den|authorize|permission denied|user denied|scope\.userLocation/i.test(errMsg);
};

// 显示授权拒绝弹窗，通过 ScreenHint 组件呈现。
const showWifiPermissionDeniedModal = () => {
  wifiAuthDeniedVisible.value = true;
};

// 用户点击「重新授权」时，打开小程序权限设置页，引导用户开启位置权限后重新扫描。
const handleReauthorize = () => {
  wifiAuthDeniedVisible.value = false;
  uni.openSetting({
    success(res) {
      // 若用户在设置页开启了位置权限，则重新启动 WiFi 扫描
      if (res.authSetting["scope.userLocation"]) {
        startScanning();
      }
    },
  });
};

// 将 iOS 当前手机连接的 WiFi 写入候选列表，供用户继续填写设备连接密码；扫描结束后重置刷新防抖标志。
const applyIOSConnectedWifi = (wifi) => {
  const ssid = wifi?.SSID;
  if (!ssid) {
    wifiList.value = [];
    currentState.value = STATES.LIST;
    isRefreshing.value = false;
    return;
  }

  wifiList.value = [
    {
      ...wifi,
      SSID: ssid,
      secure: wifi.secure !== false,
      fromCurrentWifi: true,
    },
  ];
  currentState.value = STATES.LIST;
  isRefreshing.value = false;
};

// 获取 iOS 当前手机连接的 WiFi，并把它作为可选网络展示。
const syncIOSConnectedWifi = () => {
  wx.getConnectedWifi({
    success(res) {
      console.log("[OTA WiFi] wx.getConnectedWifi success:", res);
      applyIOSConnectedWifi(res.wifi || res);
    },
    fail(err) {
      console.warn("[OTA WiFi] wx.getConnectedWifi fail:", {
        errCode: err?.errCode,
        errMsg: err?.errMsg,
        err,
      });
      wifiList.value = [];
      currentState.value = STATES.LIST;
      isRefreshing.value = false;
    },
  });
};

// 启动 iOS WiFi 能力；iOS 只用当前手机连接的 WiFi 作为候选网络。
const startIOSScanning = () => {
  wx.startWifi({
    success(res) {
      console.log("[OTA WiFi] wx.startWifi success:", res);
      syncIOSConnectedWifi();
    },
    fail(err) {
      console.error("[OTA WiFi] wx.startWifi fail:", {
        errCode: err?.errCode,
        errMsg: err?.errMsg,
        err,
      });
      if (err.errCode === 12005) showWifiBanner.value = true;
      currentState.value = STATES.LIST;
      isRefreshing.value = false;
    },
  });
};

// 启动小程序 WiFi 能力并按平台获取可用网络；也作为刷新按钮的点击处理器，重新获取最新 WiFi 列表。
// 扫描进行中时防抖：若上次扫描尚未完成则直接 return，避免重复触发导致弹窗异常。
const startScanning = () => {
  if (isRefreshing.value) return;
  isRefreshing.value = true;
  currentState.value = STATES.SCANNING;
  wifiList.value = [];
  showWifiBanner.value = false;
  connectError.value = "";

  if (isIOS) {
    startIOSScanning();
    return;
  }

  wx.startWifi({
    success(res) {
      console.log("[OTA WiFi] wx.startWifi success:", res);
      wx.getWifiList({
        success(res) {
          console.log("[OTA WiFi] wx.getWifiList request success:", res);
        },
        fail(err) {
          console.error("[OTA WiFi] wx.getWifiList fail:", {
            errCode: err?.errCode,
            errMsg: err?.errMsg,
            err,
          });
          if (isWifiPermissionDenied(err)) showWifiPermissionDeniedModal();
          if (err.errCode === 12005) showWifiBanner.value = true;
          isRefreshing.value = false;
        },
      });
    },
    fail(err) {
      console.error("[OTA WiFi] wx.startWifi fail:", {
        errCode: err?.errCode,
        errMsg: err?.errMsg,
        err,
      });
      if (isWifiPermissionDenied(err)) showWifiPermissionDeniedModal();
      if (err.errCode === 12005) showWifiBanner.value = true;
      isRefreshing.value = false;
    },
  });

  wx.onGetWifiList((res) => {
    const realWifiList = res.wifiList || [];
    if (realWifiList.length) {
      console.log("[OTA WiFi] wx.onGetWifiList received wifiList:", realWifiList);
    } else {
      console.warn("[OTA WiFi] wx.onGetWifiList received empty wifiList:", res);
    }
    wifiList.value = realWifiList;
    currentState.value = STATES.LIST;
    isRefreshing.value = false;
  });
};

// 选择列表中的 WiFi，并打开密码输入弹窗。
const selectWifi = (wifi) => {
  cancelWifiConnectPolling();
  connectingWifi.value = wifi;
  connectInput.value = { ssid: wifi.SSID, password: "" };
  connectMode.value = wifi.secure ? "secure" : "open";
  connectError.value = "";
  currentState.value = STATES.CONNECTING;
};

// 选择手动输入 WiFi，并打开手动输入弹窗。
const selectOther = () => {
  cancelWifiConnectPolling();
  connectingWifi.value = null;
  connectInput.value = { ssid: "", password: "" };
  connectMode.value = "manual";
  connectError.value = "";
  currentState.value = STATES.CONNECTING;
};

// 关闭连接弹窗，并停止当前 WiFi 连接轮询。
const closeConnectSheet = () => {
  cancelWifiConnectPolling();
  connectError.value = "";
  currentState.value = connectedWifi.value ? STATES.CONNECTED : STATES.LIST;
};

const hasChinese = (str) => /[\u4e00-\u9fa5]/.test(str);

const ssidWarning = computed(() => {
  if (connectMode.value === "manual" && hasChinese(connectInput.value.ssid)) {
    return "网络名称仅支持英文字符及数字，请连接英文名网络或把网络改为英文";
  }
  return "";
});

// 判断当前是否禁止点击加入按钮，提交中也禁止重复点击。
const joinDisabled = computed(() => {
  if (isSubmittingWifi.value) return true;
  if (connectMode.value === "secure") return !connectInput.value.password;
  if (connectMode.value === "manual") return !connectInput.value.ssid;
  return false;
});

// 按当前页面状态限制 WiFi 列表高度，避免列表撑开整页。
const wifiListScrollHeight = computed(() => {
  if (currentState.value === STATES.SCANNING) return "auto";
  const itemCount = wifiList.value.length + 1;
  const maxHeight = currentState.value === STATES.CONNECTED ? 276 : 420;
  return `${Math.min(itemCount * 92, maxHeight)}rpx`;
});

// 清理 WiFi 连接轮询定时器。
const clearWifiConnectTimer = () => {
  clearTimeout(wifiConnectTimer);
  wifiConnectTimer = null;
  wifiConnectPollCount = 0;
};

// 取消当前 WiFi 连接轮询，并恢复弹窗提交状态。
const cancelWifiConnectPolling = () => {
  wifiConnectRequestId += 1;
  clearWifiConnectTimer();
  connectStatusText.value = "";
  isSubmittingWifi.value = false;
  uni.hideLoading();
};

// 判断设备在线且已连 WiFi；online:true + netType:"4g" 表示设备已切网但 WiFi 连接失败。
const isDeviceConnectedByWifi = (deviceStatus) => {
  if (deviceStatus?.online !== true) return false;
  const netType = String(deviceStatus?.netType || "").toLowerCase();
  if (netType === "4g") return "net_fail";
  return netType === "wifi";
};

// 轮询设备电量接口，等待设备切到 WiFi 在线；netType:4g 快速失败，超时 30 次后放弃。
const waitForDeviceWifiConnected = (requestId) => {
  return new Promise((resolve) => {
    const poll = async () => {
      if (requestId !== wifiConnectRequestId) {
        resolve(false);
        return;
      }

      wifiConnectPollCount += 1;
      try {
        const deviceStatus = await getDeviceBatteryAPI();
        if (requestId !== wifiConnectRequestId) {
          resolve(false);
          return;
        }
        const connResult = isDeviceConnectedByWifi(deviceStatus);
        // online:true + netType:wifi → 成功
        if (connResult === true) {
          resolve(true);
          return;
        }
        // online:true + netType:4g → 立即失败（设备已切 4g，WiFi 连不上）
        if (connResult === "net_fail") {
          resolve(false);
          return;
        }
        // online:false + netType:"" → 继续轮询
        // online:true + netType:"" → 忽略，继续轮询（netType 暂未上报）
      } catch (err) {
        if (requestId !== wifiConnectRequestId) {
          resolve(false);
          return;
        }
      }

      if (wifiConnectPollCount >= WIFI_CONNECT_MAX_POLL_COUNT) {
        resolve(false);
        return;
      }

      wifiConnectTimer = setTimeout(poll, WIFI_CONNECT_POLL_INTERVAL);
    };

    poll();
  });
};

// 提交 WiFi 配置给游戏设备，并轮询确认设备真实连上 WiFi 后再展示成功。
const submitDeviceWifiConfig = async ({ ssid, password }) => {
  if (isSubmittingWifi.value) return;
  wifiConnectRequestId += 1;
  const requestId = wifiConnectRequestId;
  clearWifiConnectTimer();
  isSubmittingWifi.value = true;
  connectStatusText.value = "WiFi连接中...";
  uni.showLoading({
    title: "WiFi连接中",
    mask: true,
  });
  try {
    await connectDeviceWifiAPI(ssid, password);
    const isConnected = await waitForDeviceWifiConnected(requestId);
    if (requestId !== wifiConnectRequestId) return;
    if (!isConnected) {
      connectError.value = WIFI_CONNECT_FAILED_TEXT;
      return;
    }
    connectedWifi.value = {
      ...(connectingWifi.value || {}),
      SSID: ssid,
      password,
      secure: connectMode.value === "secure" || !!password,
    };
    connectError.value = "";
    currentState.value = STATES.CONNECTED;
  } catch (err) {
    if (requestId === wifiConnectRequestId) {
      connectError.value =
        err?.code === -1 && err?.message ? err.message : WIFI_CONNECT_FAILED_TEXT;
    }
  } finally {
    if (requestId === wifiConnectRequestId) {
      clearWifiConnectTimer();
      connectStatusText.value = "";
      isSubmittingWifi.value = false;
      uni.hideLoading();
    }
  }
};

// 校验用户输入并提交 WiFi 配置，不再把手机连接结果当作设备连接成功。
const joinNetwork = () => {
  if (joinDisabled.value) return;
  connectError.value = "";
  const ssid = connectInput.value.ssid;
  const password = connectInput.value.password;

  submitDeviceWifiConfig({ ssid, password });
};

// 清理 OTA 更新相关定时器，避免页面退出或状态结束后继续执行。
const clearUpdateTimers = () => {
  clearInterval(progressTimer);
  clearTimeout(timeoutTimer);
  clearTimeout(statusTimer);
  progressTimer = null;
  timeoutTimer = null;
  statusTimer = null;
};

// 启动本地进度条动画，真实完成状态以后端任务轮询结果为准。
const startProgressAnimation = () => {
  clearInterval(progressTimer);
  progressTimer = setInterval(() => {
    if (progress.value >= 90) {
      clearInterval(progressTimer);
      return;
    }
    const increment = Math.max(0.5, 2 - progress.value / 60);
    progress.value = Math.min(90, progress.value + increment);
  }, 500);
};

// 将 OTA 更新流程标记为失败并切换到失败页面。
const failUpdate = () => {
  clearUpdateTimers();
  isStartingUpdate.value = false;
  currentState.value = STATES.FAILED;
};

// 将 OTA 更新流程标记为成功，进度补到 100% 后进入完成页面。
const completeUpdate = () => {
  clearUpdateTimers();
  isStartingUpdate.value = false;
  progress.value = 100;
  setTimeout(() => {
    currentState.value = STATES.DONE;
  }, 300);
};

// 轮询 OTA 更新任务状态，状态为处理中则继续轮询，成功或失败则结束流程。
const pollUpdateTaskStatus = (taskId) => {
  clearTimeout(statusTimer);
  statusTimer = setTimeout(async () => {
    try {
      const taskStatus = await getHardwareBoxTaskStatusAPI(taskId);
      const status = Number(taskStatus?.status);
      if (status === 2) {
        completeUpdate();
        return;
      }
      if (status === 3) {
        failUpdate();
        return;
      }
      if (status === 0 || status === 1) {
        pollUpdateTaskStatus(taskId);
        return;
      }
      failUpdate();
    } catch (err) {
      failUpdate();
    }
  }, 3000);
};

// 判断设备是否满足 OTA 更新条件，不满足时返回精确提示文案。
const getUpdateDisabledReason = (deviceStatus) => {
  if (deviceStatus?.online !== true) return "请先开启智能弓";
  if (Number(deviceStatus?.battery) <= OTA_MIN_BATTERY) return OTA_LOW_BATTERY_TEXT;
  if (String(deviceStatus?.netType || "").toLowerCase() !== "wifi") return "设备当前未连接 WiFi，请先连接 WiFi 后再更新";
  return "";
};

// 获取 OTA 更新版本信息，优先使用首页跳转传入的数据，没有传参时再请求后端版本接口。
const getOtaVersionInfo = async () => {
  if (routeOtaInfo.value.versionNumber && routeOtaInfo.value.resourceUrl) {
    return {
      needUpdate: true,
      versionNumber: routeOtaInfo.value.versionNumber,
      resourceUrl: routeOtaInfo.value.resourceUrl,
    };
  }
  return getHardwareBoxVersionAPI();
};

// 点击开始更新时先判断设备状态和版本信息，满足条件才发送 OTA 指令并开始轮询任务状态。
const startUpdate = async () => {
  if (isStartingUpdate.value) return;
  if (!connectedWifi.value) return;
  isStartingUpdate.value = true;

  try {
    const deviceStatus = await getDeviceBatteryAPI();
    const disabledReason = getUpdateDisabledReason(deviceStatus);
    if (disabledReason) {
      isStartingUpdate.value = false;
      uni.showToast({
        title: disabledReason,
        icon: "none",
      });
      return;
    }

    let versionInfo;
    try {
      versionInfo = await getOtaVersionInfo();
    } catch (err) {
      isStartingUpdate.value = false;
      uni.showToast({
        title: "获取更新版本失败，请重试",
        icon: "none",
      });
      return;
    }

    if (!versionInfo?.needUpdate) {
      isStartingUpdate.value = false;
      uni.showToast({
        title: "当前已是最新版本",
        icon: "none",
      });
      return;
    }

    currentState.value = STATES.UPDATING;
    progress.value = 0;
    startProgressAnimation();
    timeoutTimer = setTimeout(() => {
      if (currentState.value === STATES.UPDATING) {
        failUpdate();
      }
    }, 5 * 60 * 1000);

    const updateResult = await sendHardwareBoxUpdateAPI({
      versionNumber: versionInfo.versionNumber,
      wifiSsid: connectedWifi.value.SSID,
      wifiPassword: connectedWifi.value.password || "",
      resourceUrl: versionInfo.resourceUrl,
    });
    if (!updateResult?.taskId) {
      failUpdate();
      return;
    }

    pollUpdateTaskStatus(updateResult.taskId);
  } catch (err) {
    failUpdate();
  }
};

// WebSocket 成功回调保留兜底能力，触发后直接按更新完成处理。
const handleWsDone = () => {
  completeUpdate();
};

// WebSocket 失败回调保留兜底能力，触发后直接按更新失败处理。
const handleWsFail = () => {
  failUpdate();
};

// 处理更新完成返回，兼容首页 OTA 弹窗入口和设备页普通入口。
const handleDone = () => {
  const pages = getCurrentPages();
  const prevPage = pages[pages.length - 2];
  const prevVm = prevPage?.$vm;

  if (prevVm && "otaState" in prevVm && "otaVisible" in prevVm) {
    prevVm.otaState = "update_success";
    prevVm.otaVisible = true;
  }

  uni.navigateBack({ delta: 1 });
};

const handleRetry = () => {
  if (connectedWifi.value) {
    currentState.value = STATES.CONNECTED;
  } else {
    startScanning();
  }
};

// 监听系统输入法高度，用于让底部弹窗避开键盘遮挡。
const handleKeyboardHeightChange = (res) => {
  keyboardHeight.value = res?.height || 0;
};

// 切换密码显示状态前先收起键盘，避免安全键盘和普通输入法切换时复用旧高度产生黑条。
const togglePasswordVisibility = () => {
  uni.hideKeyboard({
    complete() {
      keyboardHeight.value = 0;
      showPassword.value = !showPassword.value;
    },
  });
};

// 页面加载时接收首页传入的 OTA 版本号和固件地址。
onLoad((options = {}) => {
  routeOtaInfo.value = {
    versionNumber: decodeURIComponent(options.versionNumber || ""),
    resourceUrl: decodeURIComponent(options.resourceUrl || ""),
  };
});

// 页面挂载后启动 WiFi 扫描流程。
onMounted(() => {
  if (typeof uni.onKeyboardHeightChange === "function") {
    uni.onKeyboardHeightChange(handleKeyboardHeightChange);
  }
  startScanning();
});

// iOS 从系统 WiFi 设置返回小程序后，同步当前手机连接的 WiFi。
onShow(() => {
  if (isIOS && currentState.value === STATES.LIST) {
    syncIOSConnectedWifi();
  }
});

onUnmounted(() => {
  if (typeof uni.offKeyboardHeightChange === "function") {
    uni.offKeyboardHeightChange(handleKeyboardHeightChange);
  }
  cancelWifiConnectPolling();
  clearUpdateTimers();
  wx.offGetWifiList && wx.offGetWifiList();
});
</script>

<template>
  <Container title="连接无线网络">
    <!-- WiFi 不可用 Banner -->
    <view v-if="showWifiBanner" class="wifi-banner">
      <text class="wifi-banner-text">请先开启 WiFi 后刷新重试</text>
    </view>

    <!-- SCANNING / LIST / CONNECTED -->
    <view
      v-if="currentState === 'SCANNING' || currentState === 'LIST' || currentState === 'CONNECTED'"
      class="wifi-page"
    >
      <!-- Hero: wifi1.png 单图（已内含吉祥物） -->
      <view class="hero-area">
        <image src="../static/ota/wifi1.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
      </view>

      <text class="page-title" :class="{ 'connected-page-title': currentState === 'CONNECTED' }">
        {{ currentState === 'CONNECTED' ? '连接成功' : '连接无线网络' }}
      </text>
      <text v-if="currentState !== 'CONNECTED'" class="page-subtitle">网络名称仅支持英文字符及数字</text>

      <view v-if="isIOS && currentState !== 'CONNECTED'" class="ios-guide">
        <text class="ios-guide-text">请先在系统 WiFi 设置中连接目标网络，返回后选择当前 WiFi 并填写密码</text>
      </view>

      <!-- 已连接网络卡片 -->
      <view v-if="currentState === 'CONNECTED'" class="section-label-row connected-section-label-row">
        <text class="section-label">连接网络</text>
      </view>
      <view v-if="currentState === 'CONNECTED'" class="wifi-list-card connected-wifi-card">
        <view class="wifi-item connected-wifi-item">
          <image class="check-icon" src="../static/sicon/check.png" mode="aspectFit" />
          <text class="wifi-ssid connected-wifi-ssid">{{ connectedWifi?.SSID }}</text>
          <view class="wifi-icons connected-wifi-icons">
            <image
              v-if="connectedWifi?.secure"
              class="security-icon"
              src="../static/sicon/pwd.png"
              mode="aspectFit"
            />
            <image class="signal-icon" src="../static/sicon/wifi.png" mode="aspectFit" />
          </view>
        </view>
      </view>

      <!-- 网络列表标题 -->
      <view class="section-label-row">
        <text class="section-label">网络</text>
        <image
          src="../static/sicon/refresh.png"
          mode="aspectFit"
          :style="{ width: '34rpx', height: '34rpx', marginLeft: '8rpx', opacity: isRefreshing ? 0.3 : 0.7 }"
          @click="startScanning"
        />
      </view>

      <!-- 网络列表卡片 -->
      <scroll-view
        scroll-y
        class="wifi-list-card"
        :class="{ 'wifi-list-scroll': currentState !== 'SCANNING' }"
        :style="{ height: wifiListScrollHeight }"
      >
        <block v-if="currentState === 'SCANNING'">
          <view class="wifi-item" @click="selectOther">
            <text class="wifi-ssid other-text">其他...</text>
          </view>
        </block>
        <block v-else>
          <view
            v-for="(wifi, idx) in wifiList"
            :key="wifi.SSID"
            class="wifi-item"
            @click="selectWifi(wifi)"
          >
            <text class="wifi-ssid">{{ wifi.SSID }}</text>
            <view class="wifi-icons">
              <image
                v-if="wifi.secure"
                class="security-icon"
                src="../static/sicon/pwd.png"
                mode="aspectFit"
              />
              <image class="signal-icon" src="../static/sicon/wifi.png" mode="aspectFit" />
            </view>
          </view>
          <view class="wifi-item" @click="selectOther">
            <text class="wifi-ssid other-text">其他...</text>
          </view>
        </block>
      </scroll-view>

      <!-- CONNECTED：开始更新按钮 -->
      <view v-if="currentState === 'CONNECTED'" class="bottom-btn-area connected-bottom-btn-area">
        <view class="primary-btn update-btn" @click="startUpdate">
          <text class="primary-btn-text">开始更新</text>
        </view>
      </view>
    </view>

    <!-- UPDATING -->
    <view v-else-if="currentState === 'UPDATING'" class="center-page">
      <image src="../static/ota/target-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
      <text class="page-title" style="margin-top: 24rpx;">更新中,请稍等片刻...</text>
      <view class="progress-wrap">
        <view class="progress-track">
          <view class="progress-fill" :style="{ width: progress + '%' }"></view>
        </view>
        <text class="progress-pct">{{ Math.floor(progress) }}%</text>
      </view>
    </view>

    <!-- DONE -->
    <view v-else-if="currentState === 'DONE'" class="center-page">
      <image src="../static/ota/check-char.png" mode="aspectFit" style="width: 194rpx; height: 166rpx;" />
      <text class="page-title" style="margin-top: 24rpx;">更新完成</text>
      <text class="page-desc-white">请关机并重启智能弓</text>
      <view class="primary-btn done-btn" style="margin-top:20px" @click="handleDone">
        <text class="primary-btn-text">完成</text>
      </view>
    </view>

    <!-- FAILED -->
    <view v-else-if="currentState === 'FAILED'" class="center-page">
      <image src="../static/ota/close-char.png" mode="aspectFit" style="width: 194rpx; height: 164rpx;" />
      <text class="page-title fail-title" style="margin-top: 24rpx;">更新失败</text>
      <text class="page-desc-white">请确保：</text>
      <text class="page-desc-white">1、智能弓已开启</text>
      <text class="page-desc-white">2、网路连接稳定</text>
      <view class="primary-btn done-btn" style="margin-top: 40rpx;" @click="handleRetry">
        <text class="primary-btn-text">重试</text>
      </view>
    </view>

    <!-- CONNECTING 底部弹窗 -->
    <view v-if="currentState === 'CONNECTING'" class="sheet-mask">
      <view class="sheet" :style="{ marginBottom: keyboardHeight + 'px' }" @click.stop="">
        <image src="../static/ota/ota-bg.png" mode="aspectFill" class="sheet-bg" />
        <view class="sheet-inner">

          <!-- 有密码网络 -->
          <block v-if="connectMode === 'secure'">
            <view class="sheet-header">
              <view class="sheet-nav-btn" @click="closeConnectSheet">
                <image src="../static/sicon/arrow-left.png" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
              </view>
              <text class="sheet-title">加入"{{ connectInput.ssid }}"</text>
              <view
                class="sheet-nav-btn"
                :class="{ 'nav-disabled': joinDisabled }"
                @click="joinNetwork"
              >
                <image src="../static/sicon/check.png" mode="aspectFit" style="width: 28rpx; height: 24rpx;" />
              </view>
            </view>
            <view class="input-row-card">
              <text class="input-label">密码</text>
              <input
                class="input-field"
                :password="!showPassword"
                :adjust-position="false"
                v-model="connectInput.password"
                placeholder="输入网络密码"
                placeholder-class="input-placeholder"
                confirm-type="done"
                @confirm="joinNetwork"
              />
              <!-- 密码显示/隐藏切换按钮 -->
              <view class="pwd-eye-btn" @click="togglePasswordVisibility">
                <image
                  :src="showPassword ? '../static/sicon/eye-on.png' : '../static/sicon/eye-off.png'"
                  mode="aspectFit"
                  style="width: 40rpx; height: 40rpx;"
                />
              </view>
            </view>
            <text v-if="connectStatusText" class="connect-status">{{ connectStatusText }}</text>
            <text v-if="connectError" class="connect-error">{{ connectError }}</text>
          </block>

          <!-- 无密码网络 -->
          <block v-else-if="connectMode === 'open'">
            <view class="sheet-header">
              <view class="sheet-nav-btn" @click="closeConnectSheet">
                <image src="../static/sicon/arrow-left.png" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
              </view>
              <text class="sheet-title">加入"{{ connectInput.ssid }}"</text>
              <view class="sheet-nav-btn" @click="joinNetwork">
                <image src="../static/sicon/check.png" mode="aspectFit" style="width: 28rpx; height: 24rpx;" />
              </view>
            </view>
            <text v-if="connectStatusText" class="connect-status">{{ connectStatusText }}</text>
            <text v-else class="sheet-hint">该网络为开放网络，点击 ✓ 加入</text>
            <text v-if="connectError" class="connect-error">{{ connectError }}</text>
          </block>

          <!-- 手动输入 -->
          <block v-else-if="connectMode === 'manual'">
            <view class="sheet-header">
              <view class="sheet-nav-btn" @click="closeConnectSheet">
                <image src="../static/sicon/arrow-left.png" mode="aspectFit" style="width: 40rpx; height: 40rpx;" />
              </view>
              <text class="sheet-title">加入无线网络</text>
              <view
                class="sheet-nav-btn"
                :class="{ 'nav-disabled': joinDisabled }"
                @click="joinNetwork"
              >
                <image src="../static/sicon/check.png" mode="aspectFit" style="width: 28rpx; height: 24rpx;" />
              </view>
            </view>
            <view class="input-row-card">
              <text class="input-label">名称</text>
              <input
                class="input-field"
                v-model="connectInput.ssid"
                placeholder="输入网络名称"
                placeholder-class="input-placeholder"
              />
            </view>
            <view class="input-row-card">
              <text class="input-label">密码</text>
              <input
                class="input-field"
                :password="!showPassword"
                :adjust-position="false"
                v-model="connectInput.password"
                placeholder="输入网络密码"
                placeholder-class="input-placeholder"
                confirm-type="done"
                @confirm="joinNetwork"
              />
              <!-- 密码显示/隐藏切换按钮 -->
              <view class="pwd-eye-btn" @click="togglePasswordVisibility">
                <image
                  :src="showPassword ? '../static/sicon/eye-on.png' : '../static/sicon/eye-off.png'"
                  mode="aspectFit"
                  style="width: 40rpx; height: 40rpx;"
                />
              </view>
            </view>
            <text v-if="ssidWarning" class="connect-error">{{ ssidWarning }}</text>
            <text v-else-if="connectStatusText" class="connect-status">{{ connectStatusText }}</text>
            <text v-else-if="connectError" class="connect-error">{{ connectError }}</text>
          </block>

        </view>
      </view>
    </view>

    <!-- 授权拒绝弹窗 -->
    <ScreenHint :show="wifiAuthDeniedVisible" :onClose="() => (wifiAuthDeniedVisible = false)">
      <view class="wifi-auth-denied">
        <text class="wifi-auth-denied-text">拒绝授权获取wifi列表失败，请重新授权或手动输入</text>
        <view class="wifi-auth-denied-btns">
          <view class="wifi-auth-denied-btn cancel-btn" @click="wifiAuthDeniedVisible = false">
            <text>取消授权</text>
          </view>
          <view class="wifi-auth-denied-btn confirm-btn" @click="handleReauthorize">
            <text>重新授权</text>
          </view>
        </view>
      </view>
    </ScreenHint>
  </Container>
</template>

<style scoped>
.wifi-auth-denied {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 32rpx 40rpx 24rpx;
  gap: 32rpx;
}
.wifi-auth-denied-text {
  color: #fff;
  font-size: 28rpx;
  text-align: center;
  line-height: 1.6;
}
.wifi-auth-denied-btns {
  display: flex;
  flex-direction: row;
  gap: 24rpx;
  width: 100%;
  justify-content: center;
}
.wifi-auth-denied-btn {
  flex: 1;
  height: 72rpx;
  border-radius: 36rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28rpx;
}
.wifi-auth-denied-btn.cancel-btn {
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
}
.wifi-auth-denied-btn.confirm-btn {
  background: rgba(254, 216, 71, 1);
  color: #1a1a2e;
  font-weight: 600;
}

.wifi-banner {
  background-color: rgba(255, 200, 0, 0.12);
  border: 1rpx solid rgba(254, 216, 71, 0.5);
  border-radius: 8rpx;
  padding: 16rpx 24rpx;
  margin: 16rpx 24rpx 0;
}
.wifi-banner-text {
  color: rgba(254, 216, 71, 1);
  font-size: 26rpx;
}

.wifi-page {
  padding: 0 67rpx 40rpx;
  display: flex;
  flex-direction: column;
}

.hero-area {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 40rpx 0 24rpx;
}

.page-title {
  color: rgba(91, 196, 255, 1);
  font-size: 36rpx;
  font-weight: 600;
  text-align: center;
  margin-bottom: 8rpx;
}
.connected-page-title {
  color: #4DD9C8;
}
.page-subtitle {
  color: rgba(255, 255, 255, 0.5);
  font-size: 24rpx;
  text-align: center;
  margin-bottom: 32rpx;
}
.fail-title {
  color: rgba(255, 100, 100, 1);
}

.ios-guide {
  background-color: rgba(95, 173, 255, 0.12);
  border-radius: 8rpx;
  padding: 16rpx 24rpx;
  margin-bottom: 20rpx;
}
.ios-guide-text {
  color: rgba(95, 173, 255, 1);
  font-size: 26rpx;
}

.section-label-row {
  display: flex;
  align-items: center;
  margin-bottom: 24rpx;
  padding: 0 22rpx;
}
.connected-section-label-row {
  margin-top: 32rpx;
}
.section-label {
  color: rgba(255, 255, 255, 0.56);
  font-size: 28rpx;
}

.wifi-list-card {
  background-color: rgba(30, 35, 50, 0.96);
  border-radius: 24rpx;
  overflow: hidden;
  margin-bottom: 20rpx;
}
.wifi-list-scroll {
  flex-shrink: 0;
}
.connected-wifi-card {
  background-color: rgba(35, 40, 56, 0.96);
  margin-bottom: 44rpx;
}
.wifi-item {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 92rpx;
  padding: 0 26rpx 0 66rpx;
}
.wifi-item:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 66rpx;
  right: 26rpx;
  bottom: 0;
  height: 1rpx;
  background-color: rgba(255, 255, 255, 0.08);
}
.connected-wifi-item {
  min-height: 88rpx;
  padding-left: 28rpx;
}
.wifi-ssid {
  color: rgba(255, 255, 255, 1);
  font-size: 28rpx;
  line-height: 36rpx;
  flex: 1;
}
.connected-wifi-ssid {
  font-weight: 500;
}
.other-text {
  color: rgba(255, 255, 255, 0.86);
}
.wifi-icons {
  display: flex;
  align-items: center;
  gap: 12rpx;
  flex-shrink: 0;
}
.connected-wifi-icons {
  gap: 14rpx;
}
.check-icon {
  width: 28rpx;
  height: 24rpx;
  margin-right: 16rpx;
  flex-shrink: 0;
}
.security-icon {
  width: 24rpx;
  height: 30rpx;
  flex-shrink: 0;
}
.signal-icon {
  width: 36rpx;
  height: 32rpx;
  flex-shrink: 0;
}

.bottom-btn-area {
  margin-top: 24rpx;
}
.connected-bottom-btn-area {
  display: flex;
  justify-content: center;
  margin-top: 20rpx;
}

.primary-btn {
  background-color: rgba(254, 216, 71, 1);
  border-radius: 50rpx;
  height: 88rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.update-btn {
  width: 412rpx;
}
.primary-btn-text {
  color: rgba(0, 0, 0, 1);
  font-size: 30rpx;
  font-weight: 500;
}

.center-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 80rpx 32rpx 40rpx;
}
.page-desc-white {
  color: rgba(255, 255, 255, 1);
  font-size: 28rpx;
  line-height: 52rpx;
  text-align: center;
}
.done-btn {
  width: 80%;
}

.progress-wrap {
  width: 80%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16rpx;
  margin-top: 40rpx;
}
.progress-track {
  width: 100%;
  height: 10rpx;
  background-color: rgba(255, 255, 255, 0.15);
  border-radius: 5rpx;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  background-color: rgba(91, 196, 255, 1);
  border-radius: 5rpx;
  transition: width 0.4s ease;
}
.progress-pct {
  color: rgba(91, 196, 255, 1);
  font-size: 28rpx;
  font-weight: 600;
}

.sheet-mask {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 999;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.sheet {
  position: relative;
  background-color: rgba(57, 47, 29, 1);
  border-top-left-radius: 28rpx;
  border-top-right-radius: 28rpx;
  overflow: hidden;
  min-height: 320rpx;
  border: 1rpx solid rgba(249, 213, 161, 0.4);
  border-bottom: none;
}
.sheet-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}
.sheet-inner {
  position: relative;
  z-index: 1;
  padding: 32rpx 32rpx 112rpx;
}

.sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 32rpx;
}
.sheet-nav-btn {
  width: 64rpx;
  height: 64rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}
.nav-disabled {
  opacity: 0.3;
}
.sheet-title {
  color: rgba(255, 255, 255, 1);
  font-size: 30rpx;
  font-weight: 600;
  flex: 1;
  text-align: center;
}
.sheet-hint {
  color: rgba(255, 255, 255, 0.7);
  font-size: 26rpx;
  text-align: center;
  margin-bottom: 24rpx;
}
.connect-status {
  color: rgba(255, 255, 255, 0.72);
  font-size: 26rpx;
  line-height: 40rpx;
  margin-top: 16rpx;
  display: block;
  text-align: center;
}
.connect-error {
  color: rgba(254, 216, 71, 1);
  font-size: 26rpx;
  line-height: 40rpx;
  margin-top: 16rpx;
  display: block;
  /* 错误提示文案居中展示 */
  text-align: center;
}

.input-row-card {
  background-color: rgba(255, 255, 255, 0.1);
  border-radius: 16rpx;
  display: flex;
  align-items: center;
  padding: 24rpx 24rpx;
  margin-left: 48rpx;
  margin-right: 48rpx;
}
.input-row-card + .input-row-card {
  margin-top: 16rpx;
}
.input-label {
  color: rgba(255, 255, 255, 0.8);
  font-size: 28rpx;
  width: 80rpx;
  flex-shrink: 0;
}
.input-field {
  flex: 1;
  color: rgba(255, 255, 255, 1);
  font-size: 28rpx;
  background: transparent;
  border: none;
  padding: 0 8rpx;
}
.input-placeholder {
  color: rgba(255, 255, 255, 0.3);
}

/* 密码查看切换按钮 */
.pwd-eye-btn {
  width: 56rpx;
  height: 56rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
</style>
