import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
import { API_URL } from "./api";

const AuthCtx = createContext(null);
const UI_MODE_KEY = "nexapg_ui_mode";

function loadStorage() {
  try {
    return JSON.parse(localStorage.getItem("nexapg_auth") || "null");
  } catch {
    return null;
  }
}

function loadUiMode() {
  try {
    const value = localStorage.getItem(UI_MODE_KEY);
    if (value === "easy" || value === "dba") return value;
  } catch {
    // ignore storage errors
  }
  return "dba";
}

export function AuthProvider({ children }) {
  const initial = loadStorage();
  const [tokens, setTokens] = useState(initial?.tokens || null);
  const [me, setMe] = useState(initial?.me || null);
  const [uiMode, setUiModeState] = useState(loadUiMode);
  const [alertStatus, setAlertStatus] = useState({ warnings: [], alerts: [], warning_count: 0, alert_count: 0 });
  const [alertToasts, setAlertToasts] = useState([]);
  const [serviceInfo, setServiceInfo] = useState(null);
  const knownAlertKeysRef = useRef(new Set());
  const hasAlertSnapshotRef = useRef(false);

  const persist = (nextTokens, nextMe) => {
    if (nextTokens && nextMe) {
      localStorage.setItem("nexapg_auth", JSON.stringify({ tokens: nextTokens, me: nextMe }));
    } else {
      localStorage.removeItem("nexapg_auth");
    }
  };

  const refresh = async () => {
    if (!tokens?.refreshToken) return null;
    const res = await fetch(`${API_URL}/auth/refresh`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ refresh_token: tokens.refreshToken }),
    });
    if (!res.ok) {
      setTokens(null);
      setMe(null);
      persist(null, null);
      return null;
    }
    const data = await res.json();
    const nextTokens = { accessToken: data.access_token, refreshToken: data.refresh_token };
    setTokens(nextTokens);
    persist(nextTokens, me);
    return nextTokens;
  };

  const login = async (email, password) => {
    const res = await fetch(`${API_URL}/auth/login`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email, password }),
    });
    if (!res.ok) throw new Error("Login failed");
    const data = await res.json();
    const nextTokens = { accessToken: data.access_token, refreshToken: data.refresh_token };
    const meRes = await fetch(`${API_URL}/me`, {
      headers: { Authorization: `Bearer ${nextTokens.accessToken}` },
    });
    if (!meRes.ok) throw new Error("Could not load user profile");
    const profile = await meRes.json();
    setTokens(nextTokens);
    setMe(profile);
    persist(nextTokens, profile);
  };

  const logout = async () => {
    try {
      if (tokens?.accessToken) {
        await fetch(`${API_URL}/auth/logout`, {
          method: "POST",
          headers: { Authorization: `Bearer ${tokens.accessToken}` },
        });
      }
    } finally {
      setTokens(null);
      setMe(null);
      persist(null, null);
    }
  };

  const dismissAlertToast = (toastId) => {
    setAlertToasts((prev) => prev.map((t) => (t.id === toastId ? { ...t, closing: true } : t)));
    setTimeout(() => {
      setAlertToasts((prev) => prev.filter((t) => t.id !== toastId));
    }, 220);
  };

  useEffect(() => {
    if (!tokens?.accessToken) {
      setAlertStatus({ warnings: [], alerts: [], warning_count: 0, alert_count: 0 });
      setAlertToasts([]);
      knownAlertKeysRef.current = new Set();
      hasAlertSnapshotRef.current = false;
      return;
    }

    let mounted = true;

    const pushToastsForNewItems = (items) => {
      if (!items.length) return;
      const createdAt = Date.now();
      const nextToasts = items.slice(0, 4).map((item, idx) => ({
        id: `${createdAt}-${idx}-${item.alert_key}`,
        alertKey: item.alert_key,
        severity: item.severity,
        title: item.name,
        target: item.target_name,
        message: item.message,
        closing: false,
      }));
      setAlertToasts((prev) => [...nextToasts, ...prev].slice(0, 6));
      for (const toast of nextToasts) {
        setTimeout(() => {
          if (!mounted) return;
          dismissAlertToast(toast.id);
        }, 8000);
      }
    };

    const loadAlertStatus = async () => {
      const request = async (accessToken) =>
        fetch(`${API_URL}/alerts/status`, {
          headers: { Authorization: `Bearer ${accessToken}` },
        });

      let res = await request(tokens.accessToken);
      if (res.status === 401 && tokens.refreshToken) {
        const refreshed = await refresh();
        if (refreshed?.accessToken) {
          res = await request(refreshed.accessToken);
        }
      }
      if (!res.ok) return;

      const payload = await res.json();
      if (!mounted) return;
      setAlertStatus(payload);

      const currentItems = [...(payload.warnings || []), ...(payload.alerts || [])];
      const currentKeys = new Set(currentItems.map((item) => item.alert_key));
      if (!hasAlertSnapshotRef.current) {
        knownAlertKeysRef.current = currentKeys;
        hasAlertSnapshotRef.current = true;
        return;
      }
      const newItems = currentItems.filter((item) => !knownAlertKeysRef.current.has(item.alert_key));
      knownAlertKeysRef.current = currentKeys;
      pushToastsForNewItems(newItems);
    };

    loadAlertStatus().catch(() => {});
    const timer = setInterval(() => {
      loadAlertStatus().catch(() => {});
    }, 8000);

    return () => {
      mounted = false;
      clearInterval(timer);
    };
  }, [tokens?.accessToken, tokens?.refreshToken]);

  useEffect(() => {
    if (!tokens?.accessToken) {
      setServiceInfo(null);
      return;
    }

    let mounted = true;

    const request = async (path, method = "GET") => {
      const doFetch = async (accessToken) =>
        fetch(`${API_URL}${path}`, {
          method,
          headers: { Authorization: `Bearer ${accessToken}` },
        });

      let res = await doFetch(tokens.accessToken);
      if (res.status === 401 && tokens.refreshToken) {
        const refreshed = await refresh();
        if (refreshed?.accessToken) {
          res = await doFetch(refreshed.accessToken);
        }
      }
      if (!res.ok) return null;
      return res.json();
    };

    const runServiceCheck = async () => {
      await request("/service/info/check", "POST");
      const info = await request("/service/info", "GET");
      if (mounted && info) setServiceInfo(info);
    };

    runServiceCheck().catch(() => {});
    const timer = setInterval(() => {
      runServiceCheck().catch(() => {});
    }, 30000);

    return () => {
      mounted = false;
      clearInterval(timer);
    };
  }, [tokens?.accessToken, tokens?.refreshToken]);

  const setUiMode = (nextMode) => {
    const mode = nextMode === "easy" ? "easy" : "dba";
    setUiModeState(mode);
    localStorage.setItem(UI_MODE_KEY, mode);
  };

  const value = useMemo(
    () => ({
      tokens,
      me,
      login,
      logout,
      refresh,
      uiMode,
      setUiMode,
      alertStatus,
      alertToasts,
      dismissAlertToast,
      serviceInfo,
      serviceUpdateAvailable: !!serviceInfo?.update_available,
    }),
    [tokens, me, uiMode, alertStatus, alertToasts, serviceInfo]
  );
  return <AuthCtx.Provider value={value}>{children}</AuthCtx.Provider>;
}

export function useAuth() {
  const ctx = useContext(AuthCtx);
  if (!ctx) throw new Error("useAuth must be used inside AuthProvider");
  return ctx;
}
