<script setup lang="ts">
import { App as CapacitorApp, type AppInfo } from "@capacitor/app";

const { user, loading } = useAuth();
const { initializeTheme } = useTheme();
const {
  ensurePushSubscription,
  getPushStatus,
  triggerInstallPrompt,
  canTriggerInstallPrompt,
  isNativeApp,
  initializeWebPushRouting,
  consumePendingPushRoute
} = usePush();

type AppVersionPayload = {
  latestVersion: string | null;
  minSupportedVersion: string | null;
  updateUrl: string | null;
  message: string | null;
};

const promptDismissed = ref(false);
const installPromptDismissed = ref(false);
const promptLoading = ref(false);
const installLoading = ref(false);
const promptMessage = ref("");
const showPushPrompt = ref(false);
const showInstallPrompt = ref(false);
const installHelpMessage = ref("");
const showUpdatePrompt = ref(false);
const isUpdateRequired = ref(false);
const updateLoading = ref(false);
const updateMessage = ref("");
const updateUrl = ref("");
let updateCheckIntervalId: ReturnType<typeof window.setInterval> | null = null;
let lastUpdateCheckAt = 0;
let appStateChangeListener: Awaited<ReturnType<typeof CapacitorApp.addListener>> | null = null;

const UPDATE_CHECK_INTERVAL_MS = 60_000;

const parseVersion = (value: string) =>
  value
    .split(".")
    .map((part) => Number.parseInt(part.replace(/\D+/g, ""), 10))
    .map((part) => (Number.isFinite(part) ? part : 0));

const compareVersions = (left: string, right: string) => {
  const leftParts = parseVersion(left);
  const rightParts = parseVersion(right);
  const maxLength = Math.max(leftParts.length, rightParts.length);

  for (let index = 0; index < maxLength; index += 1) {
    const leftPart = leftParts[index] ?? 0;
    const rightPart = rightParts[index] ?? 0;

    if (leftPart > rightPart) return 1;
    if (leftPart < rightPart) return -1;
  }

  return 0;
};

const formatPushError = (error: unknown) => {
  if (!(error instanceof Error)) {
    return "Не удалось подключить уведомления";
  }

  if (error.message.includes("/public/push-subscriptions") && error.message.includes("404")) {
    return "На сервере пока нет публичного push-endpoint. Нужно обновить backend до последней версии.";
  }

  if (error.message.includes("/public/push-subscriptions") && error.message.includes("400")) {
    return "Сервер отклонил push-подписку. Обычно это значит, что backend на сервере ещё не обновлён или работает старая версия API.";
  }

  return error.message;
};

const checkAppVersion = async () => {
  if (!process.client || !isNativeApp()) {
    showUpdatePrompt.value = false;
    return;
  }

  try {
    const [appInfo, versionInfo] = await Promise.all([
      CapacitorApp.getInfo(),
      useApi<AppVersionPayload>("/app-version")
    ]);

    const currentVersion = (appInfo as AppInfo).version?.trim();
    const latestVersion = versionInfo.latestVersion?.trim();
    const minSupportedVersion = versionInfo.minSupportedVersion?.trim();

    if (!currentVersion || (!latestVersion && !minSupportedVersion)) {
      showUpdatePrompt.value = false;
      return;
    }

    isUpdateRequired.value = Boolean(
      minSupportedVersion && compareVersions(currentVersion, minSupportedVersion) < 0
    );

    const hasOptionalUpdate = Boolean(
      latestVersion && compareVersions(currentVersion, latestVersion) < 0
    );

    showUpdatePrompt.value = isUpdateRequired.value || hasOptionalUpdate;
    updateUrl.value = versionInfo.updateUrl?.trim() || "";
    updateMessage.value = versionInfo.message?.trim() || "Доступна новая версия приложения";
  } catch {
    showUpdatePrompt.value = false;
  }
};

const checkAppVersionThrottled = async (force = false) => {
  if (!process.client || !isNativeApp()) {
    return;
  }

  const now = Date.now();
  if (!force && now - lastUpdateCheckAt < UPDATE_CHECK_INTERVAL_MS) {
    return;
  }

  lastUpdateCheckAt = now;
  await checkAppVersion();
};

const refreshPromptState = async () => {
  if (!process.client || loading.value) {
    showPushPrompt.value = false;
    showInstallPrompt.value = false;
    return;
  }

  if (!user.value) {
    showPushPrompt.value = false;
    showInstallPrompt.value = false;
    promptMessage.value = "";
    return;
  }

  const status = await getPushStatus();

  showInstallPrompt.value = false;
  showPushPrompt.value = false;
  installHelpMessage.value = "";

  if (isNativeApp()) {
    return;
  }

  if (status.installRequired && !installPromptDismissed.value) {
    showInstallPrompt.value = true;

    if (status.isIos) {
      installHelpMessage.value =
        "На iPhone и iPad push-уведомления работают только после установки приложения на экран «Домой». Откройте меню Safari и выберите «На экран Домой».";
    } else if (!status.canInstall) {
      installHelpMessage.value =
        "Если кнопки установки нет, откройте меню браузера на телефоне и выберите «Установить приложение» или «Добавить на главный экран».";
    }
  }

  if (!status.supported) {
    return;
  }

  if (status.permission === "denied") {
    promptMessage.value = "Уведомления запрещены в браузере. Их можно включить в настройках сайта.";
    return;
  }

  if (!showInstallPrompt.value) {
    showPushPrompt.value = !promptDismissed.value && (!status.hasBrowserSubscription || !status.hasServerSubscription);
  }
};

const enableNotifications = async () => {
  promptLoading.value = true;
  promptMessage.value = "";

  try {
    await ensurePushSubscription();
    showPushPrompt.value = false;
    promptDismissed.value = true;
    promptMessage.value = "Уведомления подключены";
  } catch (error) {
    promptMessage.value = formatPushError(error);
  } finally {
    promptLoading.value = false;
  }
};

const installApplication = async () => {
  installLoading.value = true;
  installHelpMessage.value = "";

  try {
    const accepted = await triggerInstallPrompt();
    if (!accepted) {
      installHelpMessage.value = "Установка была отменена. Можно вернуться к этому позже.";
      return;
    }

    installPromptDismissed.value = true;
    showInstallPrompt.value = false;
    promptMessage.value = "Приложение установлено. Теперь можно включить push-уведомления.";
    await refreshPromptState();
  } catch (error) {
    installHelpMessage.value = error instanceof Error ? error.message : "Не удалось открыть окно установки";
  } finally {
    installLoading.value = false;
  }
};

const openUpdateUrl = async () => {
  if (!process.client || !updateUrl.value) {
    return;
  }

  updateLoading.value = true;

  try {
    window.location.assign(updateUrl.value);
  } finally {
    updateLoading.value = false;
  }
};

const dismissPrompt = () => {
  promptDismissed.value = true;
  showPushPrompt.value = false;
};

const dismissInstallPrompt = () => {
  installPromptDismissed.value = true;
  showInstallPrompt.value = false;
};

const dismissUpdatePrompt = () => {
  if (isUpdateRequired.value) {
    return;
  }

  showUpdatePrompt.value = false;
};

onMounted(async () => {
  initializeTheme();
  initializeWebPushRouting();
  void refreshPromptState();
  void consumePendingPushRoute();
  void checkAppVersionThrottled(true);

  if (!process.client) {
    return;
  }

  document.addEventListener("visibilitychange", handleVisibilityChange);
  window.addEventListener("focus", handleWindowFocus);

  if (isNativeApp()) {
    appStateChangeListener = await CapacitorApp.addListener("appStateChange", handleAppStateChange);
  }

  updateCheckIntervalId = window.setInterval(() => {
    void checkAppVersionThrottled();
  }, UPDATE_CHECK_INTERVAL_MS);
});

onBeforeUnmount(() => {
  if (!process.client) {
    return;
  }

  document.removeEventListener("visibilitychange", handleVisibilityChange);
  window.removeEventListener("focus", handleWindowFocus);

  if (updateCheckIntervalId !== null) {
    window.clearInterval(updateCheckIntervalId);
    updateCheckIntervalId = null;
  }

  void appStateChangeListener?.remove();
  appStateChangeListener = null;
});

const handleAppStateChange = ({ isActive }: { isActive: boolean }) => {
  if (!isActive) {
    return;
  }

  void checkAppVersionThrottled(true);
};

const handleVisibilityChange = () => {
  if (document.visibilityState !== "visible") {
    return;
  }

  void checkAppVersionThrottled();
};

const handleWindowFocus = () => {
  void checkAppVersionThrottled();
};

watch([user, loading], () => {
  promptMessage.value = "";
  void refreshPromptState();
  void consumePendingPushRoute();
});
</script>

<template>
  <NuxtLayout>
    <div v-if="showUpdatePrompt" :class="isUpdateRequired ? 'app-update app-update--blocking' : 'push-consent app-update'">
      <div class="push-consent__content">
        <strong>{{ isUpdateRequired ? "Нужно обновить приложение" : "Доступно обновление" }}</strong>
        <p>{{ updateMessage }}</p>
        <p v-if="isUpdateRequired">Текущая версия больше не поддерживается. Обновите приложение, чтобы продолжить работу.</p>
      </div>
      <div class="push-consent__actions">
        <button :disabled="updateLoading || !updateUrl" @click="openUpdateUrl">
          {{ updateLoading ? "Открываем..." : "Обновить приложение" }}
        </button>
        <button
          v-if="!isUpdateRequired"
          class="secondary"
          :disabled="updateLoading"
          @click="dismissUpdatePrompt"
        >
          Позже
        </button>
      </div>
    </div>

    <div v-if="showInstallPrompt" class="push-consent push-consent--install">
      <div class="push-consent__content">
        <strong>Установить приложение на телефон?</strong>
        <p>
          Чтобы уведомления приходили на мобильном устройстве, откройте сервис как установленное PWA-приложение.
        </p>
        <p v-if="installHelpMessage" class="push-consent__hint">{{ installHelpMessage }}</p>
      </div>
      <div class="push-consent__actions">
        <button v-if="canTriggerInstallPrompt()" :disabled="installLoading" @click="installApplication">
          {{ installLoading ? "Открываем..." : "Установить" }}
        </button>
        <button class="secondary" :disabled="installLoading" @click="dismissInstallPrompt">Не сейчас</button>
      </div>
    </div>

    <div v-if="showPushPrompt" class="push-consent">
      <div class="push-consent__content">
        <strong>Получать уведомления о новых матчах и сигналах?</strong>
        <p>
          При появлении новых матчей в списке мы будем отправлять push-уведомления в браузер и в установленное приложение.
        </p>
      </div>
      <div class="push-consent__actions">
        <button :disabled="promptLoading" @click="enableNotifications">
          {{ promptLoading ? "Подключаем..." : "Да, включить" }}
        </button>
        <button class="secondary" :disabled="promptLoading" @click="dismissPrompt">Не сейчас</button>
      </div>
    </div>

    <p
      v-if="promptMessage && !isUpdateRequired"
      class="push-consent__message"
      :style="{ marginTop: 'calc(0.5rem + env(safe-area-inset-top, 0px))' }"
    >
      {{ promptMessage }}
    </p>

    <NuxtPage v-if="!isUpdateRequired" />
  </NuxtLayout>
</template>
