<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 BowPower from "@/components/BowPower.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,
  MATCH_WS_AUDIO_ACK_EVENT,
  MATCH_WS_STATE_EVENT,
} 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 DEFAULT_READY_TIME = 15;
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);
const readyTime = ref(DEFAULT_READY_TIME);
let halfRestTimer = null;
/** 控制设备离线提示弹窗的显示状态 */
const showOfflineModal = ref(false);
let battleEnded = false;
let skipNextRestoreOnShow = false;
let restoreGeneration = 0;

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

function leaveHalfRest() {
  clearHalfRestCountdown();
  halfTimeTip.value = false;
  halfRest.value = false;
}

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 timeoutTime = normalizeTimestamp(battleInfo?.timeoutTime);
  if (timeoutTime) {
    const elapsedSeconds = Math.max(0, (Date.now() - timeoutTime) / 1000);
    return Math.max(
      0,
      Math.min(HALF_REST_SECONDS, Math.ceil(HALF_REST_SECONDS - elapsedSeconds))
    );
  }

  const endTime = normalizeTimestamp(
    battleInfo?.halfRestEndTime ?? battleInfo?.restEndTime
  );
  if (endTime) {
    const diffSeconds = (endTime - Date.now()) / 1000;
    return Math.max(
      0,
      Math.min(HALF_REST_SECONDS, Math.ceil(diffSeconds))
    );
  }

  return HALF_REST_SECONDS;
}

function startHalfRestCountdown(seconds = HALF_REST_SECONDS) {
  clearHalfRestCountdown();
  const secondsValue = Number(seconds);
  const normalizedSeconds = Number.isFinite(secondsValue)
    ? secondsValue
    : HALF_REST_SECONDS;
  halfRestRemain.value = Math.max(
    0,
    Math.min(HALF_REST_SECONDS, Math.ceil(normalizedSeconds))
  );

  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 normalizeTimestamp(value) {
  const numberValue = Number(value || 0);
  if (!numberValue) return 0;
  return numberValue < 1000000000000 ? numberValue * 1000 : numberValue;
}

function getReadyTime(battleInfo) {
  const value = Number(battleInfo?.readyTime);
  return Number.isFinite(value) && value > 0 ? value : DEFAULT_READY_TIME;
}

function getReadyRemainingSeconds(battleInfo) {
  const total = getReadyTime(battleInfo);
  const countdownStartTime = normalizeTimestamp(battleInfo?.countdownStartTime);
  if (!countdownStartTime) return total;

  const elapsed = Math.max(0, (Date.now() - countdownStartTime) / 1000);
  console.log('11111111111111111111111111111',  total, countdownStartTime,  Math.max(0, (Date.now() - countdownStartTime) / 1000))
  return Math.max(0, total - elapsed);
}

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, 4].includes(status)) return;
  if (!battleInfo?.serverAddr) return;

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

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

function notifyMatchAudioAck(message) {
  if (
    message?.sequence === undefined ||
    message?.sequence === null ||
    message?.sequence === ""
  ) {
    return;
  }

  uni.$emit(MATCH_WS_AUDIO_ACK_EVENT, {
    matchId: message.matchId || battleId.value,
    sequence: message.sequence,
    matchWsType: message.matchWsType,
    matchWsTypeName: message.matchWsTypeName,
  });
}

/**
 * 监听设备在线状态，大乱斗比赛进行中设备离线时弹窗提示用户
 */
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) {
      readyTime.value = getReadyTime(battleInfo);
      setTimeout(() => {
        uni.$emit("update-timer", getReadyRemainingSeconds(battleInfo));
      }, 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;
    }
    leaveHalfRest();
    if (force) {
      const ackTime = normalizeTimestamp(battleInfo.current?.ackTime);
      const remain = ackTime ? Math.max(0, (Date.now() - ackTime) / 1000) : 0;
      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);
  }
}

async function restoreLatestBattle() {
  if (!battleId.value) return;

  const currentRestoreId = ++restoreGeneration;
  let result = null;
  try {
    result = normalizeBattleInfo(await getBattleAPI(battleId.value));
  } catch (err) {
    console.log("restore latest melee battle failed:", err);
    return;
  }
  if (currentRestoreId !== restoreGeneration || !result) return;

  if (result.status === 2) {
    battleEnded = true;
    uni.showToast({
      title: "比赛已结束",
      icon: "none",
    });
    uni.navigateBack({
      delta: 2,
    });
    return;
  }

  reconnectMatchServer(result);
  recoverData(result, { force: true });
}

function onMatchSocketState(event) {
  if (event?.state !== "open" || event?.reconnected !== true) return;
  if (
    event.matchId &&
    battleId.value &&
    String(event.matchId) !== String(battleId.value)
  ) {
    return;
  }
  restoreLatestBattle();
}

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) {
    skipNextRestoreOnShow = true;
    reconnectMatchServer(readySnapshot);
    recoverData(readySnapshot);
  }
  // uni.enableAlertBeforeUnload({
  //   message: "离开比赛可能导致比赛失败，是否继续？",
  //   success: (res) => {
  //     console.log("已启用离开提示");
  //   },
  // });
});

async function onReceiveMessage(msg) {
  if (Array.isArray(msg)) return;
  if (msg.type === MESSAGETYPESV2.AboutToStart) {
    recoverData(msg);
  } else if (msg.type === MESSAGETYPESV2.BattleStart) {
    leaveHalfRest();
    recoverData(msg);
  } else if (msg.type === MESSAGETYPESV2.ShootResult) {
    recoverData(msg);
    if (msg.shootData?.threeConsecutive10Rings === true) {
      nextTick(() => audioManager.play("tententen", false));
    }
  } else if (msg.type === MESSAGETYPESV2.HalfRest) {
    halfTimeTip.value = true;
    halfRest.value = true;
    tips.value = "准备下半场";
    startHalfRestCountdown(getHalfRestSeconds(msg));
  } else if (msg.type === MESSAGETYPESV2.BattleEnd) {
    battleEnded = true;
    notifyMatchAudioAck(msg);
    setTimeout(() => {
      // 全部跳转到新结算页
      uni.redirectTo({
        url: "/pages/friend-battle-result?battleId=" + msg.matchId,
      });
    }, 1000);
  }
}
onMounted(async () => {
  uni.setKeepScreenOn({
    keepScreenOn: true,
  });
  uni.$on("socket-inbox", onReceiveMessage);
  uni.$on(MATCH_WS_STATE_EVENT, onMatchSocketState);
  await laserCloseAPI();
});
onBeforeUnmount(() => {
  uni.setKeepScreenOn({
    keepScreenOn: false,
  });
  restoreGeneration += 1;
  clearHalfRestCountdown();
  uni.$off("socket-inbox", onReceiveMessage);
  uni.$off(MATCH_WS_STATE_EVENT, onMatchSocketState);
  closeBattleServer("melee-battle-unmount");
  audioManager.stopAll();
});

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

onShow(() => {
  if (skipNextRestoreOnShow) {
    skipNextRestoreOnShow = false;
    return;
  }
  restoreLatestBattle();
});
</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"
        :count="readyTime"
      />
      <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"
        stable-shot-effect
      />
      <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>
