import React, { createContext, useContext, useEffect, useMemo, useState } from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ZinoClient } from "./client";
import { AuthService } from "./auth";
import { WorkflowService } from "./workflow";
import { ViewService } from "./views";
import type { User } from "./types";

// ---------------------------------------------------------------------------
// Context
// ---------------------------------------------------------------------------

export interface ZinoContextValue {
  client: ZinoClient;
  auth: AuthService;
  workflows: WorkflowService;
  views: ViewService;
  user: User | null;
  setUser: (user: User | null) => void;
}

const ZinoContext = createContext<ZinoContextValue | null>(null);

const MOCK_SESSION_KEY = "zino_mock_session";

// Shared QueryClient — sensible defaults for generated apps.
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 30_000,       // 30s before refetch
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
});

// ---------------------------------------------------------------------------
// Provider
// ---------------------------------------------------------------------------

export interface ZinoProviderProps {
  baseUrl: string;
  children: React.ReactNode;
}

/**
 * Wrap your app with `<ZinoProvider>` to get access to Zino services
 * and TanStack Query throughout the component tree.
 *
 * ```tsx
 * <ZinoProvider baseUrl="http://localhost:8091">
 *   <App />
 * </ZinoProvider>
 * ```
 */
export function ZinoProvider({ baseUrl, children }: ZinoProviderProps) {
  const [user, setUserState] = useState<User | null>(null);

  const viteMock = import.meta.env.VITE_ZINO_MOCK === "true";
  const resolvedUrl = baseUrl === "mock" || viteMock ? "mock" : baseUrl;
  const isMock = resolvedUrl === "mock";

  const setUser = useMemo(() => (u: User | null) => {
    setUserState(u);
    if (isMock) {
      if (u) {
        sessionStorage.setItem(MOCK_SESSION_KEY, "1");
      } else {
        sessionStorage.removeItem(MOCK_SESSION_KEY);
      }
    }
  }, [isMock]);

  // Restore session on mount — from localStorage token or mock sessionStorage.
  useEffect(() => {
    if (isMock && sessionStorage.getItem(MOCK_SESSION_KEY)) {
      const authSvc = new AuthService(new ZinoClient({ baseUrl: "mock" }));
      const mockUser = authSvc.getCurrentUser();
      if (mockUser) setUserState(mockUser);
    } else {
      // Real mode — check if a token exists in localStorage
      const client = new ZinoClient({ baseUrl: resolvedUrl });
      if (client.getToken()) {
        const authSvc = new AuthService(client);
        const restored = authSvc.getCurrentUser();
        if (restored) setUserState(restored);
      }
    }
  }, []);

  const value = useMemo<ZinoContextValue>(() => {
    const client = new ZinoClient({
      baseUrl: resolvedUrl,
      onAuthError: () => setUser(null),
    });

    return {
      client,
      auth: new AuthService(client),
      workflows: new WorkflowService(client),
      views: new ViewService(client),
      user,
      setUser,
    };
  }, [resolvedUrl, setUser]);

  const ctx = useMemo(
    () => ({ ...value, user, setUser }),
    [value, user, setUser],
  );

  return (
    <QueryClientProvider client={queryClient}>
      <ZinoContext.Provider value={ctx}>{children}</ZinoContext.Provider>
    </QueryClientProvider>
  );
}

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

/**
 * Access Zino services. Use with TanStack Query:
 *
 * ```tsx
 * const { views } = useZino();
 * const { data } = useQuery({
 *   queryKey: ['recordview', rvId],
 *   queryFn: () => views.getTabularView(rvId, { page: 1 }),
 * });
 * ```
 */
export function useZino(): ZinoContextValue {
  const ctx = useContext(ZinoContext);
  if (!ctx) {
    throw new Error("useZino must be used within a <ZinoProvider>");
  }
  return ctx;
}

/** Re-export queryClient for direct invalidation in mutations. */
export { queryClient };
