<script setup>
import { ref, nextTick, onMounted, onBeforeUnmount } from "vue";
import { onHide, onLoad, onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import BowTarget from "@/components/BowTarget.vue";
import ScorePanel from "@/components/ScorePanel.vue";
import ScoreResult from "@/components/ScoreResult.vue";
import SButton from "@/components/SButton.vue";
import Avatar from "@/components/Avatar.vue";
import BowPower from "@/components/BowPower.vue";
import TestDistance from "@/components/TestDistance.vue";
import BubbleTip from "@/components/BubbleTip.vue";
import audioManager from "@/audioManager";

import {
  createPractiseAPI,
  endPractiseAPI,
  getCurrentPractiseAPI,
  getPractiseAPI,
  startPractiseAPI,
} from "@/apis";
import {
  connectMatchWebSocket,
  closeMatchWebSocket,
  setMatchAppHideResumable,
  MATCH_WS_PRACTICE_SYNC_EVENT,
  MATCH_WS_STATE_EVENT,
} from "@/matchWebsocket";
import { sharePractiseData } from "@/canvas";
import { wxShare, debounce } from "@/util";
import { MESSAGETYPESV2 } from "@/constants";

import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user, device } = storeToRefs(store);

const start = ref(false);
const scores = ref([]);
const isSvip = ref(false);
const total = 36;
const practiseResult = ref({});
const practiseId = ref("");
const serverAddr = ref("");
const showGuide = ref(false);
const targetType = ref(1);
const sharing = ref(false);
const exiting = ref(false);
const hiddenWhileActive = ref(false);
const resumeInFlight = ref(false);
const foregroundResumeSyncPending = ref(false);
const pageVisible = ref(true);
const appHideResumable = ref(false);
const restoringSnapshot = ref(false);
let practiceSyncTimer = null;
let waitingPracticeSync = false;
const PRACTICE_SYNC_TIMEOUT_MS = 5000;
const RESULT_TIP_CDN = "https://static.shelingxingqiu.com/shootmini/static";

onLoad((options) => {
  if (options.target) {
    targetType.value = Number(options.target);
  }
});

const setPracticeAppHideResumable = (enabled) => {
  appHideResumable.value = enabled === true;
  setMatchAppHideResumable(appHideResumable.value);
};

const clearPracticeSyncTimer = () => {
  if (!practiceSyncTimer) return;
  clearTimeout(practiceSyncTimer);
  practiceSyncTimer = null;
};

const cancelPracticeSyncWait = () => {
  waitingPracticeSync = false;
  clearPracticeSyncTimer();
};

const startPracticeSyncTimer = () => {
  clearPracticeSyncTimer();
  waitingPracticeSync = true;
  practiceSyncTimer = setTimeout(() => {
    practiceSyncTimer = null;
    if (!waitingPracticeSync) return;

    waitingPracticeSync = false;
    foregroundResumeSyncPending.value = false;
    hiddenWhileActive.value = false;
    closeMatchWebSocket({
      reason: "legacy-practice-resume-timeout",
      sendLeave: false,
    });
    uni.showToast({
      title: "训练重连失败，请重试",
      icon: "none",
    });
  }, PRACTICE_SYNC_TIMEOUT_MS);
};

const connectPracticeServer = () => {
  const latestServerAddr = String(serverAddr.value || "").trim();
  if (!practiseId.value || !latestServerAddr) return false;

  cancelPracticeSyncWait();
  closeMatchWebSocket({
    reason: "legacy-practice-switch",
    sendLeave: false,
  });
  connectMatchWebSocket({
    serverAddr: latestServerAddr,
    matchId: practiseId.value,
    userId: user.value.id,
    requestPracticeInfoOnOpen: true,
    appHideResumable: true,
  });
  setPracticeAppHideResumable(true);
  return true;
};

const createPractise = async () => {
  closeMatchWebSocket({ reason: "practice-recreate" });
  const result = await createPractiseAPI(
    total,
    3600,
    targetType.value,
    device.value.deviceId
  );
  if (result?.id) practiseId.value = result.id;
  if (!result?.serverAddr) {
    uni.showToast({
      title: "练习连接信息异常，请重试",
      icon: "none",
    });
    return null;
  }
  serverAddr.value = result.serverAddr;
  connectPracticeServer();
  return result;
};

const beginPractise = async () => {
  if (!practiseId.value) {
    uni.showToast({
      title: "练习未创建，请重试",
      icon: "none",
    });
    return false;
  }
  await startPractiseAPI(practiseId.value);
  return true;
};

const onReady = async () => {
  const result = await beginPractise();
  if (!result) return;
  scores.value = [];
  isSvip.value = false;
  start.value = true;
  audioManager.play("练习开始");
};

const onOver = async (message) => {
  setPracticeAppHideResumable(false);
  hiddenWhileActive.value = false;
  foregroundResumeSyncPending.value = false;
  cancelPracticeSyncWait();
  practiseResult.value = Array.isArray(message?.details)
    ? message
    : await getPractiseAPI(practiseId.value);
  start.value = false;
};

async function onReceiveMessage(msg) {
  if (msg.type === MESSAGETYPESV2.ShootResult) {
    isSvip.value = msg.sVip === true;
    scores.value = Array.isArray(msg.details) ? msg.details : scores.value;
  } else if (msg.type === MESSAGETYPESV2.BattleEnd) {
    setPracticeAppHideResumable(false);
    hiddenWhileActive.value = false;
    foregroundResumeSyncPending.value = false;
    cancelPracticeSyncWait();
    setTimeout(() => onOver(msg), 1500);
  }
  // messages.forEach((msg) => {
  //   if (msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
  //     if (scores.value.length < total) {
  //       scores.value.push(msg.target);
  //       if (practiseId && scores.value.length === total / 2) {
  //         showGuide.value = true;
  //         setTimeout(() => {
  //           showGuide.value = false;
  //         }, 3000);
  //       }
  //       if (scores.value.length === total) {
  //         setTimeout(onOver, 1500);
  //       }
  //     }
  //   }
  // });
}

async function onComplete() {
  setPracticeAppHideResumable(false);
  const validArrows = (practiseResult.value.details || []).filter(
    (a) => a.x !== -30 && a.y !== -30
  );
  if (validArrows.length === total) {
    uni.navigateBack();
  } else {
    practiseId.value = "";
    serverAddr.value = "";
    practiseResult.value = {};
    start.value = false;
    scores.value = [];
    isSvip.value = false;
    await createPractise();
  }
}

async function exitPractise() {
  if (exiting.value) return;
  exiting.value = true;
  setPracticeAppHideResumable(false);
  hiddenWhileActive.value = false;
  foregroundResumeSyncPending.value = false;
  cancelPracticeSyncWait();

  try {
    if (practiseId.value && !practiseResult.value?.details) {
      await endPractiseAPI(practiseId.value);
    }
  } catch (error) {
    console.error("Failed to stop practice", error);
  } finally {
    uni.navigateBack();
  }
}

const stopMissingCurrentPracticeAndExit = async () => {
  if (exiting.value) return;

  const localPractiseId = practiseId.value;
  exiting.value = true;
  setPracticeAppHideResumable(false);
  hiddenWhileActive.value = false;
  foregroundResumeSyncPending.value = false;
  cancelPracticeSyncWait();

  try {
    if (localPractiseId) {
      await endPractiseAPI(localPractiseId);
    }
  } catch (error) {
    console.error("Failed to stop missing current practice", error);
  } finally {
    closeMatchWebSocket({ reason: "legacy-practice-current-missing" });
    practiseId.value = "";
    serverAddr.value = "";
    practiseResult.value = {};
    start.value = false;
    scores.value = [];
    isSvip.value = false;
    uni.showToast({
      title: "训练已结束，请重新进入",
      icon: "none",
    });
    uni.navigateBack();
  }
};

const resumeCurrentPractice = async () => {
  if (
    resumeInFlight.value ||
    !hiddenWhileActive.value ||
    !appHideResumable.value ||
    exiting.value
  ) {
    return;
  }

  resumeInFlight.value = true;
  try {
    let currentPractice;
    try {
      currentPractice = await getCurrentPractiseAPI();
    } catch (error) {
      if (!pageVisible.value || exiting.value) return;
      console.error("Failed to get current practice", error);
      await stopMissingCurrentPracticeAndExit();
      return;
    }

    if (!pageVisible.value || !hiddenWhileActive.value || exiting.value) return;

    const latestPractiseId = currentPractice?.id;
    const latestServerAddr = String(currentPractice?.serverAddr || "").trim();
    if (
      currentPractice === null ||
      !latestPractiseId ||
      !latestServerAddr
    ) {
      await stopMissingCurrentPracticeAndExit();
      return;
    }

    practiseId.value = latestPractiseId;
    serverAddr.value = latestServerAddr;
    foregroundResumeSyncPending.value = true;
    if (!connectPracticeServer()) {
      foregroundResumeSyncPending.value = false;
      await stopMissingCurrentPracticeAndExit();
    }
  } finally {
    resumeInFlight.value = false;
  }
};

const onMatchSocketState = (event = {}) => {
  if (event.state !== "open") return;
  if (
    event.matchId &&
    String(event.matchId) !== String(practiseId.value)
  ) {
    return;
  }
  startPracticeSyncTimer();
};

const onPracticeInfoSync = async (payload = {}) => {
  const responsePractiseId = String(
    payload.matchId || payload.practiceInfo?.id || ""
  );
  if (
    !responsePractiseId ||
    responsePractiseId !== String(practiseId.value)
  ) {
    return;
  }

  const snapshot = payload.practiceInfo;
  if (!snapshot || typeof snapshot !== "object") return;

  cancelPracticeSyncWait();
  foregroundResumeSyncPending.value = false;
  hiddenWhileActive.value = false;

  restoringSnapshot.value = true;
  await nextTick();
  try {
    scores.value = Array.isArray(snapshot.details) ? snapshot.details : [];
    isSvip.value = snapshot.sVip === true;

    const status = Number(snapshot.status);
    if (status === 1) {
      start.value = false;
    } else if (status === 2) {
      start.value = true;
    }
  } finally {
    restoringSnapshot.value = false;
    await nextTick();
  }

  const timeLimit = Number(snapshot.timeLimit);
  const duration = Number(snapshot.duration);
  if (Number(snapshot.status) === 2 && Number.isFinite(timeLimit) && timeLimit > 0) {
    uni.$emit(
      "update-remain",
      Math.max(0, timeLimit - (Number.isFinite(duration) ? duration : 0))
    );
  }
};

const getResultTipSrc = (result = {}) => {
  const validCount = (result.details || []).filter(
    (arrow) => arrow.x !== -30 && arrow.y !== -30
  ).length;
  return `${RESULT_TIP_CDN}/${validCount < total ? "2un" : ""}finish-tip.png`;
};

const onClickShare = debounce(async () => {
  if (sharing.value) return;
  sharing.value = true;
  try {
    await sharePractiseData("shareCanvas", 3, user.value, practiseResult.value);
    await wxShare("shareCanvas");
  } catch (e) {
    uni.showToast({
      title: "海报生成失败，请稍后重试",
      icon: "none",
    });
  } finally {
    sharing.value = false;
  }
});

onHide(() => {
  pageVisible.value = false;
  if (
    !appHideResumable.value ||
    exiting.value ||
    !practiseId.value ||
    practiseResult.value?.details
  ) {
    return;
  }

  hiddenWhileActive.value = true;
});

onShow(async () => {
  pageVisible.value = true;
  await resumeCurrentPractice();
});

onMounted(async () => {
  void audioManager.warmAll();
  uni.setKeepScreenOn({
    keepScreenOn: true,
  });
  uni.$on("socket-inbox", onReceiveMessage);
  uni.$on(MATCH_WS_PRACTICE_SYNC_EVENT, onPracticeInfoSync);
  uni.$on(MATCH_WS_STATE_EVENT, onMatchSocketState);
  uni.$on("share-image", onClickShare);
  await createPractise();
});

onBeforeUnmount(() => {
  setPracticeAppHideResumable(false);
  cancelPracticeSyncWait();
  uni.setKeepScreenOn({
    keepScreenOn: false,
  });
  uni.$off("socket-inbox", onReceiveMessage);
  uni.$off(MATCH_WS_PRACTICE_SYNC_EVENT, onPracticeInfoSync);
  uni.$off(MATCH_WS_STATE_EVENT, onMatchSocketState);
  uni.$off("share-image", onClickShare);
  audioManager.stopAll();
  closeMatchWebSocket({ reason: "practice-leave" });
});
</script>

<template>
  <Container
    :bgType="1"
    title="日常耐力挑战"
    :showBottom="!start && !scores.length"
    :onBack="exitPractise"
  >
    <view>
      <TestDistance v-if="!start && !practiseResult.id" />
      <block v-else>
        <ShootProgress
          :tips="`请连续射${total}支箭`"
          :start="start"
          :total="3600"
          :onStop="onOver"
          end-audio-key="练习结束"
        />
        <view class="user-row">
          <Avatar :src="user.avatar" :size="35" />
          <BubbleTip v-if="showGuide" type="normal2">
            <text>完成过半，胜利</text>
            <text>在望！💪</text>
          </BubbleTip>
          <BowPower />
        </view>
        <BowTarget
          v-if="!restoringSnapshot"
          :currentRound="scores.length"
          :totalRound="start ? total : 0"
          :scores="scores"
          :isSvip="isSvip"
        />
        <ScorePanel
          v-if="start"
          :arrows="scores"
          :total="total"
          :rowCount="total / 4"
          :margin="1.5"
          :font-size="20"
        />
        <ScoreResult
          v-if="practiseResult.details"
          :total="total"
          :rowCount="9"
          :onClose="onComplete"
          :result="practiseResult"
          :tipSrc="getResultTipSrc(practiseResult)"
        />
        <canvas class="share-canvas" id="shareCanvas" type="2d"></canvas>
      </block>
    </view>
    <template #bottom>
      <SButton :onClick="onReady">准备好了，直接开始</SButton>
    </template>
  </Container>
</template>

<style scoped></style>
