import { createContext, useContext, useState, useEffect } from 'react';
import { jwtDecode } from 'jwt-decode';
import { USER_addres } from '../api/API';

/**
 * Лёгкий AuthContext для студии Рублокса (opensource-версия).
 *
 * Главное отличие от минки:
 *  - НЕТ UserService.validateAndRefreshToken (нет refresh-токенов в opensource).
 *  - Если JWT в localStorage просрочен — просто считаем юзера гостем
 *    и редиректим на VITE_RUBLOX_HOME при попытке зайти в защищённый раздел.
 *  - Profile-данные подгружаются один раз через GET /api-user/api/v1/users/profile.
 *
 * Все эндпоинты бэкенда конфигурируются через .env (VITE_API_BASE).
 */

const AuthContext = createContext(null);

export const useAuth = () => {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth должен использоваться внутри <AuthProvider>');
  return ctx;
};

function decodeJwtSafe(raw) {
  try {
    const p = jwtDecode(raw);
    if (p.exp && p.exp * 1000 < Date.now()) return null;
    return p;
  } catch {
    return null;
  }
}

export function AuthProvider({ children }) {
  const [state, setState] = useState({
    isAuthenticated: false,
    isLoading: true,
    user: null,
    role: 'user',
    jwt: null,
  });

  useEffect(() => {
    let cancelled = false;

    const env = (typeof import.meta !== 'undefined' && import.meta.env) || {};

    // STANDALONE — мокаем гостевого юзера, без запросов.
    // Включается либо через .env (VITE_STANDALONE=true), либо через ?standalone=1
    // в URL (для быстрого dev-доступа без правки env-файлов).
    const urlStandalone = new URLSearchParams(window.location.search).get('standalone') === '1';
    if (String(env.VITE_STANDALONE).toLowerCase() === 'true' || urlStandalone) {
      setState({
        isAuthenticated: true,
        isLoading: false,
        user: { id: 0, firstName: 'Гость', _standalone: true },
        role: 'admin',
        jwt: 'standalone',
      });
      return;
    }

    // SSO через ticket: если редиректнули с #ticket=<hex> с минки или другого
    // нашего сайта — обмениваем одноразовый ticket на JWT через бэк, кладём
    // в localStorage и чистим hash. После этого работаем как обычно.
    const ticketMatch = /(?:^|[#&])ticket=([0-9a-fA-F]{32})/.exec(window.location.hash || '');
    const cleanTicketFromUrl = () => {
      try {
        window.history.replaceState(null, '', window.location.pathname + window.location.search);
      } catch {}
    };

    // После успешного входа: если есть запомненная collab-ссылка и мы сейчас
    // НЕ на ней — переходим. Это и есть «возврат на приглашение» (минкин логин
    // сам этого не делает). Чистим ключ, чтобы не зациклить.
    const maybeResumePendingCollab = () => {
      try {
        const pending = localStorage.getItem('kbn_pending_collab_url');
        if (!pending) return;
        localStorage.removeItem('kbn_pending_collab_url');
        // уже на нужной collab-странице? не дёргаем
        if (window.location.href === pending) return;
        const u = new URL(pending);
        // безопасность: переходим только в пределах этого же origin (студии)
        if (u.origin !== window.location.origin) return;
        if (!/[?&]collab=/.test(pending)) return;
        window.location.replace(pending);
      } catch {}
    };

    const startWithJwt = (raw) => {
      const payload = decodeJwtSafe(raw);
      if (!payload) {
        localStorage.removeItem('Authorization');
        setState({ isAuthenticated: false, isLoading: false, user: null, role: 'user', jwt: null });
        return;
      }
      fetch(`${USER_addres}/api/v1/users/profile`, {
        headers: { Authorization: raw },
      })
      .then((r) => (r.ok ? r.json() : null))
      .then((data) => {
        if (cancelled) return;
        const profile = data?.data || data;
        if (!profile) {
          setState({ isAuthenticated: false, isLoading: false, user: null, role: 'user', jwt: null });
          return;
        }
        setState({
          isAuthenticated: true,
          isLoading: false,
          user: profile,
          role: profile.role || 'user',
          jwt: raw,
        });
        // Возврат на инвайт-ссылку после входа: минка вернула нас в студию
        // (на главную/кабинет), но мы запомнили collab-URL — идём на него.
        maybeResumePendingCollab();
      })
      .catch(() => {
        if (cancelled) return;
        setState({ isAuthenticated: false, isLoading: false, user: null, role: 'user', jwt: null });
      });
    };

    if (ticketMatch) {
      const ticket = ticketMatch[1];
      cleanTicketFromUrl();
      fetch(`${USER_addres}/api/v1/auth/redeem-ticket`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ticket }),
      })
        .then((r) => (r.ok ? r.json() : null))
        .then((data) => {
          if (cancelled) return;
          const jwt = data?.jwt;
          if (jwt) {
            try {
              localStorage.setItem('Authorization', jwt);
              localStorage.setItem('player_jwt', jwt);
            } catch {}
            startWithJwt(jwt);
          } else {
            // ticket уже использован или просрочен — пробуем существующий JWT
            const existing = localStorage.getItem('Authorization') || localStorage.getItem('player_jwt');
            if (existing) startWithJwt(existing);
            else setState({ isAuthenticated: false, isLoading: false, user: null, role: 'user', jwt: null });
          }
        })
        .catch(() => {
          if (cancelled) return;
          setState({ isAuthenticated: false, isLoading: false, user: null, role: 'user', jwt: null });
        });
      return () => { cancelled = true; };
    }

    const raw = localStorage.getItem('Authorization') || localStorage.getItem('player_jwt');
    if (!raw) {
      setState({ isAuthenticated: false, isLoading: false, user: null, role: 'user', jwt: null });
      return;
    }
    startWithJwt(raw);

    return () => {
      cancelled = true;
    };
  }, []);

  return <AuthContext.Provider value={state}>{children}</AuthContext.Provider>;
}

/**
 * Редирект незалогиненного пользователя на форму входа Рублокса (SSO).
 * В студии НЕТ собственной страницы /login — вход живёт на КОРНЕ домена
 * Рублокса (`https://rublox.pro/login`, НЕ `/app/login` — тот даёт 404).
 * После входа лендинг возвращает в студию/плеер с #ticket=<hex>
 * (его обменивает AuthProvider выше). `return`-параметр сохраняет ПОЛНЫЙ
 * текущий URL (включая ?collab=<token>), чтобы человек попал ровно на ту
 * инвайт-ссылку, с которой пришёл.
 */
// Ключ, под которым студия запоминает collab-ссылку на время входа.
// Основной путь возврата — rublox.pro/login читает наш ?return и сам
// редиректит обратно с #ticket (см. rublox-site LoginPage.finishLogin).
// Этот pending-ключ — РЕЗЕРВ на случай, если юзер вернулся в студию иным
// путём: AuthProvider после входа перекинет на запомненную ссылку.
export const PENDING_COLLAB_KEY = 'kbn_pending_collab_url';

export function redirectToLogin() {
  const env = (typeof import.meta !== 'undefined' && import.meta.env) || {};
  // VITE_RUBLOX_HOME = "https://rublox.pro/app" — нам нужен ORIGIN (без /app),
  // т.к. форма входа сидит на корне домена: https://rublox.pro/login.
  const rawHome = env.VITE_RUBLOX_HOME || 'https://rublox.pro/app';
  let origin;
  try {
    origin = new URL(rawHome).origin;            // https://rublox.pro
  } catch {
    origin = rawHome.replace(/\/app\/?$/, '').replace(/\/$/, '');
  }
  // Если уходим со страницы с инвайт-токеном — запомним её, чтобы вернуться
  // после входа (минка сама вернёт только в кабинет, не на эту ссылку).
  try {
    const cur = window.location.href;
    if (/[?&]collab=/.test(cur)) {
      localStorage.setItem(PENDING_COLLAB_KEY, cur);
    }
  } catch {}
  const back = encodeURIComponent(window.location.href);
  window.location.href = `${origin}/login?return=${back}`;
}
