import React, { createContext, useContext, useEffect, useRef, useState } from "react";

const AuthContext = createContext(null);

// Keycloak config
const KC_BASE = "https://web.sociowire.com:8443";
const KC_REALM = "sociowire";
const KC_CLIENT_ID = "sociowire-frontend";

const TOKEN_KEY = "sociowire:access_token";
const REFRESH_KEY = "sociowire:refresh_token";
const USERNAME_KEY = "sociowire:username";

// --- helpers ---
function decodeJwtPayload(token) {
  try {
    const parts = token.split(".");
    if (parts.length < 2) return null;
    const json = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
    return JSON.parse(json);
  } catch {
    return null;
  }
}

function willExpireSoon(token, withinSeconds = 90) {
  const p = decodeJwtPayload(token);
  if (!p || !p.exp) return true;
  const now = Math.floor(Date.now() / 1000);
  return p.exp - now <= withinSeconds;
}

async function refreshWithKeycloak(refreshToken) {
  const params = new URLSearchParams();
  params.set("grant_type", "refresh_token");
  params.set("client_id", KC_CLIENT_ID);
  params.set("refresh_token", refreshToken);

  const res = await fetch(`${KC_BASE}/realms/${KC_REALM}/protocol/openid-connect/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: params.toString(),
  });

  const text = await res.text();
  if (!res.ok) {
    let msg = text;
    try {
      const j = JSON.parse(text);
      msg = j.error_description || j.error || text;
    } catch {}
    throw new Error(`${res.status} ${msg}`);
  }

  const data = JSON.parse(text);
  if (!data.access_token) throw new Error("No access_token in refresh response");
  return {
    accessToken: data.access_token,
    refreshToken: data.refresh_token || refreshToken,
  };
}

export function AuthProvider({ children }) {
  const [booting, setBooting] = useState(true);
  const [loading, setLoading] = useState(false);
  const [authenticated, setAuthenticated] = useState(false);
  const [username, setUsername] = useState("");
  const [token, setToken] = useState(null);
  const [lastError, setLastError] = useState("");

  const usernameRef = useRef("");

  function persistAuth({ accessToken, refreshToken, user }) {
    setToken(accessToken);
    setAuthenticated(true);
    setUsername(user);
    usernameRef.current = user;
    try {
      localStorage.setItem(TOKEN_KEY, accessToken);
      if (refreshToken) localStorage.setItem(REFRESH_KEY, refreshToken);
      localStorage.setItem(USERNAME_KEY, user);
    } catch {}
  }

  function clearAuth(msg = "") {
    setAuthenticated(false);
    setToken(null);
    setUsername("");
    usernameRef.current = "";
    setLastError(msg);
    try {
      localStorage.removeItem(TOKEN_KEY);
      localStorage.removeItem(REFRESH_KEY);
      localStorage.removeItem(USERNAME_KEY);
    } catch {}
  }

  async function trySilentRefreshIfNeeded() {
    try {
      const storedToken = localStorage.getItem(TOKEN_KEY);
      const storedRefresh = localStorage.getItem(REFRESH_KEY);
      const storedUser = localStorage.getItem(USERNAME_KEY) || "";

      if (!storedToken || !storedRefresh) return false;

      if (!willExpireSoon(storedToken, 30)) {
        setToken(storedToken);
        setUsername(storedUser);
        usernameRef.current = storedUser;
        setAuthenticated(true);
        return true;
      }

      const { accessToken, refreshToken } = await refreshWithKeycloak(storedRefresh);
      persistAuth({ accessToken, refreshToken, user: storedUser });
      return true;
    } catch (e) {
      console.warn("[AUTH] silent refresh failed:", e);
      clearAuth("Session expired. Please login again.");
      return false;
    }
  }

  useEffect(() => {
    (async () => {
      try {
        const ok = await trySilentRefreshIfNeeded();
        if (!ok) {
          const storedToken = localStorage.getItem(TOKEN_KEY);
          const storedUser = localStorage.getItem(USERNAME_KEY) || "";
          if (storedToken) {
            setToken(storedToken);
            setUsername(storedUser);
            usernameRef.current = storedUser;
            setAuthenticated(true);
          }
        }
      } finally {
        setBooting(false);
      }
    })();
  }, []);

  useEffect(() => {
    if (!authenticated) return;

    const interval = setInterval(async () => {
      try {
        const t = localStorage.getItem(TOKEN_KEY);
        const rt = localStorage.getItem(REFRESH_KEY);
        if (!t || !rt) return;

        if (!willExpireSoon(t, 90)) return;

        const { accessToken, refreshToken } = await refreshWithKeycloak(rt);
        const u = localStorage.getItem(USERNAME_KEY) || usernameRef.current || "";
        persistAuth({ accessToken, refreshToken, user: u });
      } catch (e) {
        console.warn("[AUTH] auto refresh failed:", e);
        clearAuth("Session expired. Please login again.");
      }
    }, 60_000);

    return () => clearInterval(interval);
  }, [authenticated]);

  async function login(user, pass) {
    setLastError("");
    const u = (user || "").trim();
    if (!u || !pass) {
      setLastError("Username and password required.");
      return;
    }

    const params = new URLSearchParams();
    params.set("grant_type", "password");
    params.set("client_id", KC_CLIENT_ID);
    params.set("username", u);
    params.set("password", pass);

    try {
      setLoading(true);

      const res = await fetch(`${KC_BASE}/realms/${KC_REALM}/protocol/openid-connect/token`, {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: params.toString(),
      });

      const text = await res.text();
      if (!res.ok) {
        let msg = "Invalid credentials or auth error.";
        try {
          const j = JSON.parse(text);
          msg = j.error_description || j.error || msg;
        } catch {}
        clearAuth(`${res.status} ${msg}`);
        return;
      }

      const data = JSON.parse(text);
      const accessToken = data.access_token;
      const refreshToken = data.refresh_token;

      if (!accessToken) {
        clearAuth("No access token in response.");
        return;
      }

      persistAuth({ accessToken, refreshToken, user: u });
      setLastError("");
    } catch (e) {
      console.error("[AUTH] network error", e);
      clearAuth("Network error during login.");
    } finally {
      setLoading(false);
    }
  }

  function logout() {
    clearAuth("");
  }

  return (
    <AuthContext.Provider
      value={{
        booting,
        loading,
        authenticated,
        username,
        token,
        login,
        logout,
        lastError,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

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