/**
 * Фоновая сверка прав механика раз в 5 минут (У35).
 *
 * Если за время активной сессии админ снял `can_login_pwa` или вывел
 * пользователя из отдела механиков (или потерял `is_admin`), сервер
 * на ближайшем `/me` вернёт 403 с `code: "NO_PWA_ACCESS"`. В этом
 * случае хук:
 *   1) Сбрасывает access-токен (через `useAuth.setToken(null)`) —
 *      `client.ts` перехватчик 401 не сработает, но мы делаем то же
 *      сами явно.
 *   2) Кладёт информацию о ревокации в sessionStorage, чтобы экран
 *      Login смог отрендерить корректное сообщение
 *      «Ваш доступ отозван» (с кликабельным контактом саппорта).
 *   3) **Не трогает IndexedDB-черновики** — они переживают сброс
 *      токена (У35). Если доступ вернут — механик при следующем
 *      входе увидит «Продолжить незавершённый ремонт» (У18).
 *
 * Параметр интервала задан в файле и при необходимости подкручивается
 * с одного места.
 */
import { useEffect, useRef } from "react";
import { HTTPError } from "ky";
import { useAuth } from "@/store/auth";
import { getMe, type Me } from "@/api/me";

export const SYNC_INTERVAL_MS = 5 * 60 * 1000; // 5 минут (У35)

export interface RevocationInfo {
  reason: "NO_PWA_ACCESS";
  support: {
    tg_username: string | null;
    phone: string | null;
  };
  at: string; // ISO timestamp
}

export const REVOCATION_STORAGE_KEY = "mechanic-pwa.revocation";

function storeRevocation(detail: unknown): void {
  // backend кладёт detail = { code, support }
  const d = detail as { code?: string; support?: RevocationInfo["support"] } | null | undefined;
  if (!d || d.code !== "NO_PWA_ACCESS") return;
  const payload: RevocationInfo = {
    reason: "NO_PWA_ACCESS",
    support: d.support ?? { tg_username: null, phone: null },
    at: new Date().toISOString(),
  };
  try {
    sessionStorage.setItem(REVOCATION_STORAGE_KEY, JSON.stringify(payload));
  } catch {
    /* sessionStorage недоступен (приватный режим?) — silent */
  }
}

export function readRevocation(): RevocationInfo | null {
  try {
    const raw = sessionStorage.getItem(REVOCATION_STORAGE_KEY);
    return raw ? (JSON.parse(raw) as RevocationInfo) : null;
  } catch {
    return null;
  }
}

export function clearRevocation(): void {
  try {
    sessionStorage.removeItem(REVOCATION_STORAGE_KEY);
  } catch {
    /* silent */
  }
}

/**
 * Подключить к корневому компоненту PWA, чтобы фоновая сверка работала
 * пока приложение открыто. Без зависимостей — стартует один раз
 * на mount и завершается на unmount.
 */
export function useMeSync(): void {
  const token = useAuth((s) => s.token);
  const setToken = useAuth((s) => s.setToken);
  const tickInFlight = useRef(false);

  useEffect(() => {
    if (!token) return; // нечего сверять

    const tick = async () => {
      if (tickInFlight.current) return;
      tickInFlight.current = true;
      try {
        const _me: Me = await getMe();
        void _me;
      } catch (err) {
        if (err instanceof HTTPError && err.response.status === 403) {
          // backend FastAPI кладёт detail в JSON
          let detail: unknown = null;
          try {
            const body = (await err.response.json()) as { detail?: unknown };
            detail = body?.detail ?? null;
          } catch {
            /* пустое или не-JSON тело */
          }
          storeRevocation(detail);
          setToken(null);
        }
        // 401 ловится в beforeError client.ts автоматически.
      } finally {
        tickInFlight.current = false;
      }
    };

    const handleFocus = () => { void tick(); };
    document.addEventListener("visibilitychange", handleFocus);
    const id = window.setInterval(tick, SYNC_INTERVAL_MS);
    return () => {
      clearInterval(id);
      document.removeEventListener("visibilitychange", handleFocus);
    };
  }, [token, setToken]);
}

/** Вспомогательная функция для use в Login.tsx — единичная проверка после login. */
export { storeRevocation };
