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

// ─── Types ────────────────────────────────────────────────────────────────────

export interface AuthUser {
  id: string;
  email: string;
  name: string;
  roles: string[];
  token: string;
}

interface AuthContextValue {
  user: AuthUser | null;
  isLoading: boolean;
  isAuthenticated: boolean;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

// ─── Context ──────────────────────────────────────────────────────────────────

const AuthContext = createContext<AuthContextValue | null>(null);

const TOKEN_KEY = "veylant_token";

// Decode JWT payload (no verification — just read claims for UI).
function decodeJWT(token: string): Record<string, unknown> | null {
  try {
    const parts = token.split(".");
    if (parts.length !== 3) return null;
    const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
    return JSON.parse(payload) as Record<string, unknown>;
  } catch {
    return null;
  }
}

function userFromToken(token: string): AuthUser | null {
  const payload = decodeJWT(token);
  if (!payload) return null;

  // Check expiry
  const exp = payload["exp"] as number | undefined;
  if (exp && exp * 1000 < Date.now()) {
    localStorage.removeItem(TOKEN_KEY);
    return null;
  }

  const roles = Array.isArray(payload["roles"])
    ? (payload["roles"] as string[])
    : [];

  return {
    id: (payload["sub"] as string) ?? "",
    email: (payload["email"] as string) ?? "",
    name: (payload["name"] as string) ?? (payload["email"] as string) ?? "",
    roles,
    token,
  };
}

// ─── Provider ─────────────────────────────────────────────────────────────────

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(() => {
    const stored = localStorage.getItem(TOKEN_KEY);
    return stored ? userFromToken(stored) : null;
  });
  const [isLoading, setIsLoading] = useState(false);

  const login = useCallback(async (email: string, password: string) => {
    setIsLoading(true);
    try {
      const res = await fetch("/v1/auth/login", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, password }),
      });

      if (!res.ok) {
        const body = await res.json().catch(() => ({}));
        const msg = (body as { error?: { message?: string } }).error?.message ?? "Identifiants invalides";
        throw new Error(msg);
      }

      const { token } = await res.json() as { token: string };
      localStorage.setItem(TOKEN_KEY, token);
      const u = userFromToken(token);
      setUser(u);
    } finally {
      setIsLoading(false);
    }
  }, []);

  const logout = useCallback(() => {
    localStorage.removeItem(TOKEN_KEY);
    setUser(null);
  }, []);

  // Auto-logout when token expires
  useEffect(() => {
    if (!user) return;
    const payload = decodeJWT(user.token);
    if (!payload) return;
    const exp = payload["exp"] as number | undefined;
    if (!exp) return;
    const msUntilExpiry = exp * 1000 - Date.now();
    if (msUntilExpiry <= 0) {
      logout();
      return;
    }
    const timer = setTimeout(logout, msUntilExpiry);
    return () => clearTimeout(timer);
  }, [user, logout]);

  return (
    <AuthContext.Provider
      value={{ user, isLoading, isAuthenticated: !!user, login, logout }}
    >
      {children}
    </AuthContext.Provider>
  );
}

// ─── Hook ─────────────────────────────────────────────────────────────────────

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

// ─── Token accessor (for API client outside React tree) ───────────────────────

let _getToken: (() => string | null) | null = null;

export function setTokenAccessor(fn: () => string | null) {
  _getToken = fn;
}

export function getToken(): string | null {
  // Try module-level accessor first, then localStorage directly
  if (_getToken) return _getToken();
  const stored = localStorage.getItem(TOKEN_KEY);
  if (!stored) return null;
  const u = userFromToken(stored);
  return u?.token ?? null;
}

export function useRegisterTokenAccessor() {
  const auth = useAuth();
  useEffect(() => {
    setTokenAccessor(() => auth.user?.token ?? null);
  }, [auth.user]);
}
