import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Database } from "@xtablo/shared/types/database.types";
import { useNavigate } from "react-router-dom";
import { supabase } from "../lib/supabase";
import { useUser } from "../providers/UserStoreProvider";
import { useAuthedApi } from "./auth";

type Note = Database["public"]["Tables"]["notes"]["Row"];
type CreateNoteInput = Pick<Database["public"]["Tables"]["notes"]["Insert"], "title" | "content">;
type UpdateNoteInput = Pick<
  Database["public"]["Tables"]["notes"]["Update"],
  "id" | "title" | "content"
>;

export function useNotes() {
  const user = useUser();
  const { data: notes, isLoading } = useQuery<Note[]>({
    queryKey: ["notes"],
    queryFn: async () => {
      const { data, error } = await supabase
        .from("notes")
        .select("*")
        .eq("user_id", user.id)
        .is("deleted_at", null)
        .order("created_at", { ascending: false });
      if (error) throw error;
      return data as Note[];
    },
    enabled: !!user.id,
  });
  return { notes, isLoading };
}

export function useNote(id: string | undefined) {
  const user = useUser();
  const { data: note, isLoading } = useQuery<Note | null>({
    queryKey: ["notes", id],
    queryFn: async () => {
      const { data, error } = await supabase
        .from("notes")
        .select("*")
        .eq("id", id)
        .eq("user_id", user.id)
        .is("deleted_at", null);
      if (error) throw error;
      if (data.length === 0) return null;
      return data[0] as Note;
    },
    enabled: !!id,
  });
  return { note, isLoading };
}

export function useCreateNote() {
  const user = useUser();
  const navigate = useNavigate();
  const queryClient = useQueryClient();

  const { mutate, isPending } = useMutation<Note, Error, CreateNoteInput>({
    mutationFn: async (input: CreateNoteInput) => {
      const { data, error } = await supabase
        .from("notes")
        .insert({
          title: input.title,
          content: input.content,
          user_id: user.id,
        })
        .select()
        .single();
      if (error) throw error;
      return data as Note;
    },
    onSuccess: (note) => {
      // Invalidate notes list to refetch
      queryClient.invalidateQueries({ queryKey: ["notes"] });
      navigate(`/notes/${note.id}`);
    },
  });
  return { mutate, isPending };
}

/**
 * Update an existing note
 */
export function useUpdateNote() {
  const user = useUser();
  const queryClient = useQueryClient();

  return useMutation<void, Error, UpdateNoteInput>({
    mutationFn: async (input: UpdateNoteInput) => {
      const { error } = await supabase
        .from("notes")
        .update({
          title: input.title,
          content: input.content,
          updated_at: new Date().toISOString(),
        })
        .eq("id", input.id)
        .eq("user_id", user.id)
        .is("deleted_at", null);
      if (error) throw error;
      return;
    },
    onSuccess: () => {
      // Invalidate both the specific note and notes list
      queryClient.invalidateQueries({ queryKey: ["notes"] });
      queryClient.invalidateQueries({ queryKey: ["tablo-notes"] });
    },
  });
}

export function useDeleteNote() {
  const navigate = useNavigate();
  const queryClient = useQueryClient();

  const { mutate, isPending } = useMutation<void, Error, string>({
    mutationFn: async (id: string) => {
      const { error } = await supabase
        .from("notes")
        .update({ deleted_at: new Date().toISOString() })
        .eq("id", id);
      if (error) throw error;
      return;
    },
    onSuccess: () => {
      // Invalidate notes list to refetch
      queryClient.invalidateQueries({ queryKey: ["notes"] });
      navigate("/notes/create", { replace: true });
    },
  });
  return { mutate, isPending };
}

/**
 * Hook to fetch sharing settings for a specific note
 */
export function useNoteSharing(noteId: string | undefined) {
  const user = useUser();
  const { data, isLoading } = useQuery({
    queryKey: ["note-sharing", noteId],
    queryFn: async () => {
      if (!noteId) return { isPublic: false, isSharedWithAllTablos: false };

      // Fetch public sharing status
      const { data: sharedNote, error: sharedError } = await supabase
        .from("shared_notes")
        .select("is_public")
        .eq("note_id", noteId)
        .eq("user_id", user.id)
        .maybeSingle();

      if (sharedError) throw sharedError;

      // Fetch tablo sharing status (tablo_id IS NULL means shared with all tablos)
      const { data: noteAccess, error: accessError } = await supabase
        .from("note_access")
        .select("is_active, tablo_id")
        .eq("note_id", noteId)
        .eq("user_id", user.id)
        .is("tablo_id", null)
        .maybeSingle();

      if (accessError) throw accessError;

      return {
        isPublic: sharedNote?.is_public ?? false,
        isSharedWithAllTablos: noteAccess?.is_active ?? false,
      };
    },
    enabled: !!noteId && !!user.id,
  });
  return {
    isPublic: data?.isPublic,
    isSharedWithAllTablos: data?.isSharedWithAllTablos,
    isLoading,
  };
}

/**
 * Hook to update sharing settings for a note
 */
export function useUpdateNoteSharing() {
  const user = useUser();
  const queryClient = useQueryClient();

  return useMutation<
    void,
    Error,
    { noteId: string; isPublic: boolean; isSharedWithAllTablos: boolean }
  >({
    mutationFn: async ({ noteId, isPublic, isSharedWithAllTablos }) => {
      // Upsert shared_notes for public sharing
      const { error: sharedError } = await supabase.from("shared_notes").upsert(
        {
          note_id: noteId,
          user_id: user.id,
          is_public: isPublic,
          updated_at: new Date().toISOString(),
        },
        {
          onConflict: "note_id",
        }
      );

      if (sharedError) throw sharedError;

      // Handle tablo sharing
      if (isSharedWithAllTablos) {
        // First, try to update existing "all tablos" sharing record
        const { data: existingRecord, error: selectError } = await supabase
          .from("note_access")
          .select("id")
          .eq("note_id", noteId)
          .eq("user_id", user.id)
          .is("tablo_id", null)
          .maybeSingle();

        if (selectError) throw selectError;

        if (existingRecord) {
          // Update existing record
          const { error: updateError } = await supabase
            .from("note_access")
            .update({
              is_active: true,
              updated_at: new Date().toISOString(),
            })
            .eq("id", existingRecord.id);

          if (updateError) throw updateError;
        } else {
          // Insert new record
          const { error: insertError } = await supabase.from("note_access").insert({
            note_id: noteId,
            user_id: user.id,
            tablo_id: null,
            is_active: true,
            updated_at: new Date().toISOString(),
          });

          if (insertError) throw insertError;
        }
      } else {
        // Remove tablo sharing by deleting
        const { error: deleteError } = await supabase
          .from("note_access")
          .delete()
          .eq("note_id", noteId)
          .eq("user_id", user.id)
          .is("tablo_id", null);

        if (deleteError) throw deleteError;
      }
    },
    onSuccess: (_, variables) => {
      // Invalidate sharing queries
      queryClient.invalidateQueries({ queryKey: ["note-sharing", variables.noteId] });
      queryClient.invalidateQueries({ queryKey: ["tablo-notes"] });
    },
  });
}

/**
 * Hook to fetch a public note (unauthenticated access allowed)
 */
export function usePublicNote(noteId: string | undefined) {
  const { data: note, isLoading } = useQuery<Note | null>({
    queryKey: ["public-note", noteId],
    queryFn: async () => {
      if (!noteId) return null;

      // First check if the note is public
      const { data: sharedNote, error: sharedError } = await supabase
        .from("shared_notes")
        .select("note_id, is_public")
        .eq("note_id", noteId)
        .eq("is_public", true)
        .maybeSingle();

      if (sharedError) throw sharedError;
      if (!sharedNote) return null;

      // Fetch the actual note
      const { data: noteData, error: noteError } = await supabase
        .from("notes")
        .select("*")
        .eq("id", noteId)
        .is("deleted_at", null)
        .maybeSingle();

      if (noteError) throw noteError;
      return noteData as Note | null;
    },
    enabled: !!noteId,
  });

  return { note, isLoading };
}

/**
 * Hook to fetch notes shared with a specific tablo
 */
export function useTabloNotes(tabloId: string | undefined) {
  const user = useUser();
  const api = useAuthedApi();
  const { data, isLoading } = useQuery<{ notes: Note[] }>({
    queryKey: ["tablo-notes", tabloId],
    queryFn: async () => {
      if (!tabloId) return { notes: [] };

      const { data } = await api.get<{ notes: Note[] }>(`/api/v1/notes/${tabloId}`);

      return data;
    },
    enabled: !!tabloId && !!user.id,
  });

  return { notes: data?.notes, isLoading };
}
