<script setup>
import { ref, onMounted, onBeforeUnmount, watch, nextTick, computed } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import BowTarget from "@/components/BowTarget.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import BattleHeader from "@/components/BattleHeader.vue";
import PlayerScore from "@/components/PlayerScore.vue";
import SButton from "@/components/SButton.vue";
import Avatar from "@/components/Avatar.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import TestDistance from "@/components/TestDistance.vue";
import SModal from "@/components/SModal.vue";
import audioManager from "@/audioManager";
import { getBattleAPI, laserCloseAPI } from "@/apis";
import { closeMatchWebSocket, connectMatchWebSocket } from "@/matchWebsocket";
import { MESSAGETYPESV2 } from "@/constants";
import { takeMatchReturnSnapshot } from "@/utils/matchReturn";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user, online } = storeToRefs(store);
const title = ref("");
const start = ref(null);
const battleId = ref("");
/** 对战模式：1=好友约战 2=排位赛，用于结算页跳转判断 */
const way = ref(0);
const currentRound = ref(1);
const tips = ref("即将开始...");
const players = ref([]);
const playersSorted = ref([]);
const playersScores = ref([]);
const halfTimeTip = ref(false);
const halfRest = ref(false);
const HALF_REST_SECONDS = 20;
const MATCH_READY_SNAPSHOT_PREFIX = "match-ready-snapshot:";
const MATCH_STATUS_BY_TEXT = {
  MATCH_STATUS_READY: 0,
  MATCH_STATUS_STARTED: 1,
  MATCH_STATUS_END: 2,
  MATCH_STATUS_TIMEOUT: 3,
  MATCH_STATUS_UNEXPECTEDLY: 4,
};
const halfRestRemain = ref(HALF_REST_SECONDS);
let halfRestTimer = null;
/** 控制设备离线提示弹窗的显示状态 */
const showOfflineModal = ref(false);
/** 记录每位玩家当前半场连续 10 环及以上次数，key 为 playerId，用于触发 tententen 音效 */
const xRingStreaks = ref({});
let battleEnded = false;
let skipNextRestoreOnShow = false;

function clearHalfRestCountdown() {
  if (halfRestTimer) {
    clearInterval(halfRestTimer);
    halfRestTimer = null;
  }
}

function getHalfRestSeconds(battleInfo) {
  const remainCandidates = [
    battleInfo?.halfRestRemain,
    battleInfo?.halfRestRemainSeconds,
    battleInfo?.restRemain,
    battleInfo?.restRemainSeconds,
  ];

  for (const item of remainCandidates) {
    const remain = Number(item);
    if (Number.isFinite(remain) && remain > 0 && remain <= HALF_REST_SECONDS) {
      return Math.ceil(remain);
    }
  }

  const endTime = Number(battleInfo?.halfRestEndTime ?? battleInfo?.restEndTime);
  if (!Number.isFinite(endTime) || endTime <= 0) return HALF_REST_SECONDS;

  const timestamp = endTime < 1e12 ? endTime * 1000 : endTime;
  const diffSeconds = (timestamp - Date.now()) / 1000;
  if (diffSeconds > 0 && diffSeconds <= HALF_REST_SECONDS) {
    return Math.ceil(diffSeconds);
  }

  return HALF_REST_SECONDS;
}

function startHalfRestCountdown(seconds = HALF_REST_SECONDS) {
  clearHalfRestCountdown();
  halfRestRemain.value = Math.max(0, Math.ceil(Number(seconds) || HALF_REST_SECONDS));

  if (halfRestRemain.value <= 0) return;

  halfRestTimer = setInterval(() => {
    if (halfRestRemain.value <= 1) {
      halfRestRemain.value = 0;
      clearHalfRestCountdown();
      return;
    }

    halfRestRemain.value -= 1;
  }, 1000);
}
const currentPlayer = computed(() =>
  players.value.find((player) => String(player?.id) === String(user.value.id))
);
const isCurrentUserSvip = computed(() => currentPlayer.value?.sVip === true);

function getReadySnapshotKey(matchId) {
  return `${MATCH_READY_SNAPSHOT_PREFIX}${String(matchId || "")}`;
}

function normalizeBattleInfo(battleInfo) {
  if (!battleInfo || typeof battleInfo !== "object") return battleInfo;

  const statusText = battleInfo.statusText || battleInfo.status_text;
  if (
    (battleInfo.status === undefined ||
      battleInfo.status === null ||
      battleInfo.status === "") &&
    MATCH_STATUS_BY_TEXT[statusText] !== undefined
  ) {
    return {
      ...battleInfo,
      status: MATCH_STATUS_BY_TEXT[statusText],
    };
  }

  return battleInfo;
}

function hasKnownStatus(battleInfo) {
  return !(
    battleInfo?.status === undefined ||
    battleInfo?.status === null ||
    battleInfo?.status === ""
  );
}

function hasStartedSnapshot(battleInfo) {
  if (hasKnownStatus(battleInfo)) return Number(battleInfo.status) !== 0;

  const current = battleInfo?.current || {};
  return !!(
    current.playerId ||
    current.startTime ||
    current.round ||
    (Array.isArray(battleInfo?.rounds) && battleInfo.rounds.length)
  );
}

function takeReadySnapshot(matchId) {
  const key = getReadySnapshotKey(matchId);
  const snapshot = uni.getStorageSync(key);
  if (snapshot) uni.removeStorageSync(key);
  return normalizeBattleInfo(snapshot);
}

function reconnectMatchServer(battleInfo) {
  const status = Number(battleInfo?.status);
  if ([2, 3, 4].includes(status)) return;
  if (!battleInfo?.serverAddr) return;

  connectMatchWebSocket({
    serverAddr: battleInfo.serverAddr,
    matchId: battleInfo.matchId || battleId.value,
    userId: user.value.id,
  });
}

function closeBattleServer(reason) {
  if (battleEnded) return;
  closeMatchWebSocket({ reason });
}

/**
 * 监听设备在线状态，大乱斗比赛进行中设备离线时弹窗提示用户
 */
watch(online, (newVal, oldVal) => {
  if (!newVal && oldVal && start.value === true) {
    showOfflineModal.value = true;
  }
});

function recoverData(battleInfo, { force = false } = {}) {
  battleInfo = normalizeBattleInfo(battleInfo);
  if (!battleInfo) return;
  if (battleInfo.status !== undefined) {
    battleEnded = [2, 4].includes(Number(battleInfo.status));
  }
  try {
    if (battleInfo.way === 1) title.value = "好友约战 - 大乱斗";
    if (battleInfo.way === 2) title.value = "排位赛 - 大乱斗";
    // 保存 way 供结算跳转时使用
    way.value = battleInfo.way ?? 0;
    
    // 优先使用接口数据，否则使用缓存
    if (battleInfo.teams?.[0]?.players) {
      players.value = [...battleInfo.teams[0].players];
    } else {
      // 大乱斗可能存的是 players 列表
      // 这里的缓存逻辑根据 AboutToStart 消息结构可能不同，假设也是 teams[0]
      // 如果是从 match-page 过来的，match-page 只存了 teams[1] 和 [2] 给对抗模式
      // 大乱斗的匹配逻辑可能不同，暂时保持原样，只做安全保护
      players.value = []; 
    }
    
    if (hasKnownStatus(battleInfo)) {
      start.value = battleInfo.status !== 0;
    } else if (!hasStartedSnapshot(battleInfo)) {
      if (start.value !== true) start.value = false;
      return;
    } else {
      start.value = true;
    }

    if (battleInfo.status === 0) {
      const readyRemain = (Date.now() - (battleInfo.createTime || Date.now())) / 1000;
      if (readyRemain > 0 && readyRemain < 15) {
        setTimeout(() => uni.$emit("update-timer", 15 - readyRemain - 0.2), 200);
      }
      return;
    }

    const rounds = Array.isArray(battleInfo.rounds) ? battleInfo.rounds : [];
    tips.value =
      (rounds.length !== 2 ? "上" : "下") + "半场：请先射6箭";
    playersScores.value = rounds.map((r) => ({ ...r.shoots }));
    const totals = {};
    players.value.forEach((p) => {
      const total = playersScores.value.reduce((acc, round) => {
        const arr = round[p.id] || [];
        return acc + arr.length;
      }, 0);
      totals[p.id] = total;
    });
    playersSorted.value = players.value.slice().sort((a, b) => {
      return totals[b.id] - totals[a.id];
    });
    if (battleInfo.status === 3) {
      halfTimeTip.value = true;
      halfRest.value = true;
      tips.value = "准备下半场";
      startHalfRestCountdown(getHalfRestSeconds(battleInfo));
      setTimeout(() => {
        uni.$emit("update-remain", 0);
      }, 200);
      return;
    }
    if (force) {
      const remain = (Date.now() - (battleInfo.current?.startTime || Date.now())) / 1000;
      console.log(`当前轮已进行${remain}秒`);
      if (remain > 0 && remain < 90) {
        setTimeout(() => {
          uni.$emit("update-remain", 90 - remain - 0.2);
        }, 200);
      }
    }
  } catch (err) {
    console.error("recoverData error:", err);
  }
}

onLoad(async (options) => {
  const returnSnapshot = options.fromReturn ? takeMatchReturnSnapshot() : null;
  skipNextRestoreOnShow = false;
  if (returnSnapshot?.matchId) battleId.value = returnSnapshot.matchId;
  else if (options.battleId) battleId.value = options.battleId;
  if (returnSnapshot) {
    skipNextRestoreOnShow = true;
    reconnectMatchServer(returnSnapshot);
    recoverData(returnSnapshot, { force: true });
    return;
  }
  const readySnapshot = takeReadySnapshot(battleId.value);
  if (readySnapshot?.status === 0) recoverData(readySnapshot);
  // uni.enableAlertBeforeUnload({
  //   message: "离开比赛可能导致比赛失败，是否继续？",
  //   success: (res) => {
  //     console.log("已启用离开提示");
  //   },
  // });
});

/**
 * 检测指定玩家连续 10 环及以上是否达到 3 箭，达到则在环数播报入队后追加 tententen 音效
 * @param {number|string} playerId - 本次射手的 ID（大乱斗中 ShootResult 保留 playerId）
 * @param {boolean} isTenPlusRingShot - 本次射击是否为 10 环及以上
 */
function isTenPlusRing(shot) {
  return !!(shot?.ringX || Number(shot?.ring) >= 10);
}

function checkAndPlayTententen(playerId, isTenPlusRingShot) {
  if (!playerId) return;
  const id = parseInt(playerId);
  if (isTenPlusRingShot) {
    xRingStreaks.value[id] = (xRingStreaks.value[id] || 0) + 1;
    // 同一玩家连续 3 箭均为 10 环及以上，追加到环数音效队列尾部播放
    if (xRingStreaks.value[id] >= 3) {
      xRingStreaks.value[id] = 0;
      // nextTick 确保 HeaderProgress 的环数播报已入队后再追加 tententen，避免播放顺序颠倒
      nextTick(() => audioManager.play("tententen", false));
    }
  } else {
    // 低于 10 环或未上靶则重置该玩家的连续计数
    xRingStreaks.value[id] = 0;
  }
}

async function onReceiveMessage(msg) {
  if (Array.isArray(msg)) return;
  if (msg.type === MESSAGETYPESV2.AboutToStart) {
    recoverData(msg);
  } else if (msg.type === MESSAGETYPESV2.BattleStart) {
    clearHalfRestCountdown();
    halfTimeTip.value = false;
    halfRest.value = false;
    recoverData(msg);
  } else if (msg.type === MESSAGETYPESV2.ShootResult) {
    // 更新前快照各玩家本轮已射箭数，用于事后识别本次射手
    const curRound = playersScores.value[playersScores.value.length - 1] || {};
    const prevCounts = {};
    for (const pid of Object.keys(curRound)) {
      prevCounts[pid] = (curRound[pid] || []).length;
    }
    recoverData(msg);
    // 对比更新后数据找出箭数增加的玩家（即本次射手），并读取其最新箭的 ring 数据
    const newRound = playersScores.value[playersScores.value.length - 1] || {};
    let shooterId = null;
    let isTenPlusRingShot = false;
    for (const pid of Object.keys(newRound)) {
      const newLen = (newRound[pid] || []).length;
      if (newLen > (prevCounts[pid] || 0)) {
        shooterId = parseInt(pid);
        const shot = newRound[pid][newLen - 1];
        isTenPlusRingShot = isTenPlusRing(shot);
        break;
      }
    }
    // 检测同一玩家连续三箭 10 环及以上，触发 tententen 音效
    checkAndPlayTententen(shooterId, isTenPlusRingShot);
  } else if (msg.type === MESSAGETYPESV2.HalfRest) {
    halfTimeTip.value = true;
    halfRest.value = true;
    tips.value = "准备下半场";
    startHalfRestCountdown();
  } else if (msg.type === MESSAGETYPESV2.BattleEnd) {
    battleEnded = true;
    setTimeout(() => {
      // 全部跳转到新结算页
      uni.redirectTo({
        url: "/pages/friend-battle-result?battleId=" + msg.matchId,
      });
    }, 1000);
  }
}
onMounted(async () => {
  uni.setKeepScreenOn({
    keepScreenOn: true,
  });
  uni.$on("socket-inbox", onReceiveMessage);
  await laserCloseAPI();
});
onBeforeUnmount(() => {
  uni.setKeepScreenOn({
    keepScreenOn: false,
  });
  clearHalfRestCountdown();
  uni.$off("socket-inbox", onReceiveMessage);
  closeBattleServer("melee-battle-unmount");
  audioManager.stopAll();
});

onHide(() => {
  closeBattleServer("melee-battle-hide");
});

onShow(async () => {
  if (skipNextRestoreOnShow) {
    skipNextRestoreOnShow = false;
    return;
  }
  if (battleId.value) {
    const result = normalizeBattleInfo(await getBattleAPI(battleId.value));
    if (!result) return;
    if (result.status === 2) {
      battleEnded = true;
      uni.showToast({
        title: "比赛已结束",
        icon: "none",
      });
      uni.navigateBack({
        delta: 2,
      });
    } else {
      reconnectMatchServer(result);
      recoverData(result, { force: true });
    }
  }
});
</script>

<template>
  <Container :title="title" :bgType="1">
    <view class="container">
      <BattleHeader v-if="!start" :players="players" />
      <TestDistance v-if="start === false" :guide="false" :isBattle="true" />
      <ShootProgress
        :show="start"
        :start="start && !halfRest"
        :tips="tips"
        :total="90"
        :melee="true"
        :halfRest="halfRest"
        :battleId="battleId"
      />
      <view v-if="start" class="user-row">
        <Avatar :src="user.avatar" :size="35" />
        <BowPower />
      </view>
      <BowTarget
        v-if="start"
        :currentRound="
          playersScores.map((s) => s[user.id].length).reduce((a, b) => a + b, 0)
        "
        :totalRound="12"
        :scores="playersScores.map((r) => r[user.id]).flat()"
        :isSvip="isCurrentUserSvip"
        :stop="halfRest"
      />
      <view :style="{ paddingBottom: '20px' }">
        <PlayerScore
          v-if="start"
          v-for="(player, index) in playersSorted"
          :key="index"
          :player="player"
          :scores="playersScores.map((s) => s[player.id])"
        />
      </view>
      <ScreenHint
        :show="halfTimeTip"
        mode="small"
      >
        <view class="half-time-tip">
          <text>上半场结束，休息一下吧:）</text>
          <text>{{ halfRestRemain }}秒后开始下半场</text>
        </view>
      </ScreenHint>
      <!-- 设备离线提示弹窗 -->
      <SModal
        :show="showOfflineModal"
        :noBg="true"
        height="360rpx"
        :onClose="() => (showOfflineModal = false)"
      >
        <view class="offline-modal">
          <text class="offline-title">设备已离线</text>
          <text class="offline-desc">检测到设备已断开连接，请检查设备后继续比赛</text>
          <SButton @click="showOfflineModal = false">我知道了</SButton>
        </view>
      </SModal>
    </view>
  </Container>
</template>

<style scoped>
.container {
  width: 100%;
  height: 100%;
}

.offline-modal {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 60rpx 40rpx 40rpx;
  gap: 24rpx;
}

.offline-title {
  font-size: 36rpx;
  font-weight: bold;
  color: #FED847;
}

.offline-desc {
  font-size: 28rpx;
  color: #CCCCCC;
  text-align: center;
  line-height: 1.6;
}
</style>
