import { createContext, ReactNode, useContext, useMemo } from "react";
import {
  useQuery,
  useMutation,
} from "@tanstack/react-query";
import { insertUserSchema, User as SelectUser, InsertUser } from "@shared/schema";
import { apiRequest, queryClient } from "../lib/queryClient";
import { useToast } from "@/hooks/use-toast";
import { useLocation } from "wouter";

type AuthContextType = {
  user: SelectUser | null;
  isLoading: boolean;
  error: Error | null;
  loginMutation: any;
  logoutMutation: any;
  registerMutation: any;
};

type LoginData = Pick<InsertUser, "username" | "password">;

export const AuthContext = createContext<AuthContextType | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const { toast } = useToast();
  const [, setLocation] = useLocation();

  // Query for fetching user data
  const {
    data: user,
    error,
    isLoading,
  } = useQuery<SelectUser | null>({
    queryKey: ["/api/user"],
    queryFn: async () => {
      try {
        // Get JWT token from localStorage
        const token = localStorage.getItem('auth_token');
        console.log("Token in useQuery:", token ? "exists" : "not found");

        const response = await fetch("/api/user", {
          headers: {
            'Authorization': token ? `Bearer ${token}` : '',
          }
        });
        console.log("User fetch response:", response.status);

        if (response.status === 401) {
          console.log("User not authenticated");
          return null;
        }

        if (!response.ok) {
          throw new Error("Failed to fetch user data");
        }

        const userData = await response.json();
        console.log("Fetched user data:", userData);
        return userData;
      } catch (error) {
        console.error("Error fetching user:", error);
        throw error;
      }
    },
    staleTime: 5 * 60 * 1000, // Keep data fresh for 5 minutes
    gcTime: 10 * 60 * 1000, // Cache for 10 minutes
    refetchOnWindowFocus: false,
    retry: false,
  });

  const loginMutation = useMutation({
    mutationFn: async (credentials: LoginData) => {
      try {
        const res = await apiRequest("POST", "/api/login", credentials);

        if (!res.ok) {
          let errorMessage = "Login failed. Please check your credentials.";
          try {
            const errorData = await res.text();
            if (errorData) {
              errorMessage = errorData;
            }
          } catch (e) {
            console.error("Error parsing error response:", e);
          }
          throw new Error(errorMessage);
        }

        try {
          const data = await res.json();
          console.log("Login response data:", data);

          // Store JWT token first
          if (data.token) {
            localStorage.setItem('auth_token', data.token);
            console.log("Stored token in localStorage");
          }

          if (!data) {
            throw new Error("Invalid response from server");
          }
          return data;
        } catch (e) {
          console.error("Error parsing login response:", e);
          throw new Error("Invalid response from server");
        }
      } catch (error) {
        if (error instanceof Error) {
          throw error;
        }
        throw new Error("An unexpected error occurred");
      }
    },
    onSuccess: (data) => {
      console.log("Login successful, updating user data:", data);
      // Extract user data excluding token
      const { token, ...userData } = data;
      // Set the user data in the query cache
      queryClient.setQueryData(["/api/user"], userData);
      console.log("Updated user data in query cache:", userData);

      // Prefetch settings
      queryClient.prefetchQuery({ 
        queryKey: ["/api/settings"],
        queryFn: async () => {
          const response = await fetch("/api/settings");
          if (!response.ok) throw new Error("Failed to fetch settings");
          return response.json();
        }
      });

      // Redirect to home page after successful login
      setLocation("/");
    },
    onError: (error: Error) => {
      console.error("Login error:", error);
      toast({
        title: "Login failed",
        description: error.message,
        variant: "destructive",
      });
    },
  });

  const registerMutation = useMutation({
    mutationFn: async (credentials: InsertUser) => {
      const res = await apiRequest("POST", "/api/register", credentials);
      if (!res.ok) {
        const error = await res.text();
        throw new Error(error || "Registration failed. Please try again.");
      }
      const data = await res.json();

      // Store the JWT token if provided in registration response
      if (data.token) {
        localStorage.setItem('auth_token', data.token);
      }

      console.log("Registration response data:", data);
      return data;
    },
    onSuccess: (user: SelectUser) => {
      console.log("Registration successful, updating user data:", user);
      queryClient.setQueryData(["/api/user"], user);
    },
    onError: (error: Error) => {
      console.error("Registration error:", error);
      toast({
        title: "Registration failed",
        description: error.message,
        variant: "destructive",
      });
    },
  });

  const logoutMutation = useMutation({
    mutationFn: async () => {
      const res = await apiRequest("POST", "/api/logout");
      if (!res.ok) {
        throw new Error("Logout failed. Please try again.");
      }
      // Clear the JWT token on logout
      localStorage.removeItem('auth_token');
      // Clear all queries and cache
      queryClient.clear();
    },
    onSuccess: () => {
      console.log("Logout successful, clearing user data");
      queryClient.setQueryData(["/api/user"], null);
      // Redirect to login page after successful logout
      setLocation("/auth");
    },
    onError: (error: Error) => {
      console.error("Logout error:", error);
      toast({
        title: "Logout failed",
        description: error.message,
        variant: "destructive",
      });
    },
  });

  const contextValue = useMemo(
    () => ({
      user: user ?? null,
      isLoading,
      error,
      loginMutation,
      logoutMutation,
      registerMutation,
    }),
    [user, isLoading, error, loginMutation, logoutMutation, registerMutation]
  );

  return (
    <AuthContext.Provider value={contextValue}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
}