import "@mantine/core/styles.css";
import { navigate } from "vike/client/router";
import {
  AppShell,
  Burger,
  Group,
  Image,
  MantineProvider,
  NavLink,
} from "@mantine/core";
import {
  IconHome2,
  IconChevronRight,
  IconActivity,
  IconTrash,
  IconCircle,
  IconCircleFilled,
  IconTrashFilled,
  IconPlus,
} from "@tabler/icons-react";
import { useDisclosure } from "@mantine/hooks";
import theme from "./theme.js";
import logoUrl from "../assets/logo.png";
import { useStore } from "../state.js";
import { useState } from "react";
import { TRPCProvider, useTRPC } from "../trpc/client.js";
import { usePageContext } from "vike-react/usePageContext";
import "./hover.css";
import {
  QueryClient,
  QueryClientProvider,
  useMutation,
  useQuery,
  useQueryClient,
} from "@tanstack/react-query";
import {
  createTRPCClient,
  httpBatchLink,
  httpSubscriptionLink,
  splitLink,
} from "@trpc/client";
import type { AppRouter } from "../trpc/router.js";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        // With SSR, we usually want to set some default staleTime
        // above 0 to avoid refetching immediately on the client
        staleTime: 60 * 1000,
      },
    },
  });
}

let browserQueryClient: QueryClient | undefined = undefined;
function getQueryClient() {
  if (typeof window === "undefined") {
    // Server: always make a new query client
    return makeQueryClient();
  }
  // Browser: make a new query client if we don't already have one
  // This is very important, so we don't re-make a new client if React
  // suspends during the initial render. This may not be needed if we
  // have a suspense boundary BELOW the creation of the query client
  if (!browserQueryClient) browserQueryClient = makeQueryClient();
  return browserQueryClient;
}

export default function LayoutDefault({
  children,
}: {
  children: React.ReactNode;
}) {
  const pageContext = usePageContext();
  const { urlPathname } = pageContext;
  const [opened, { toggle }] = useDisclosure();

  const queryClient = getQueryClient();
  const [trpc] = useState(() =>
    createTRPCClient<AppRouter>({
      links: [
        splitLink({
          // uses the httpSubscriptionLink for subscriptions
          condition: (op) => op.type === "subscription",
          true: httpSubscriptionLink({
            url: "/api/trpc",
          }),
          false: httpBatchLink({
            url: "/api/trpc",
            methodOverride: "POST",
          }),
        }),
      ],
    })
  );

  return (
    <QueryClientProvider client={queryClient}>
      <TRPCProvider trpcClient={trpc} queryClient={queryClient}>
        <MantineProvider theme={theme}>
          <AppShell
            header={{ height: 60 }}
            navbar={{
              width: 300,
              breakpoint: "sm",
              collapsed: { mobile: !opened },
            }}
            padding="lg"
          >
            <AppShell.Header>
              <Group h="100%" px="md">
                <Burger
                  opened={opened}
                  onClick={toggle}
                  hiddenFrom="sm"
                  size="sm"
                />
                <a href="/">
                  {" "}
                  <Image h={50} fit="contain" src={logoUrl} />{" "}
                </a>
              </Group>
            </AppShell.Header>
            <AppShell.Navbar p="md">
              <NavLink href="/" label="Welcome" active={urlPathname === "/"} />
              <NavLinkChat key="chat-new" />
            </AppShell.Navbar>
            <AppShell.Main> {children} </AppShell.Main>
          </AppShell>
        </MantineProvider>
      </TRPCProvider>
    </QueryClientProvider>
  );
}

function NavLinkChat() {
  const pageContext = usePageContext();
  const { urlPathname } = pageContext;
  const trpc = useTRPC();
  const queryClient = useQueryClient();
  // const
  const startConversation = useMutation(
    trpc.chat.conversations.start.mutationOptions({
      onSuccess: () => {
        queryClient.invalidateQueries({
          queryKey: trpc.chat.conversations.fetchAll.queryKey(),
        });
      },
    })
  );
  const deleteConversation = useMutation(
    trpc.chat.conversations.deleteOne.mutationOptions({
      onMutate: async ({ id: conversationIdToDelete }) => {
        /** Cancel affected queries that may be in-flight: */
        await queryClient.cancelQueries({
          queryKey: trpc.chat.conversations.fetchAll.queryKey(),
        });

        /** Optimistically update the affected queries in react-query's cache: */
        const previousConversations = await queryClient.getQueryData(
          trpc.chat.conversations.fetchAll.queryKey()
        );
        if (!previousConversations) {
          return {
            previousConversations: [],
            newConversations: [],
          };
        }
        const newConversations = previousConversations.filter(
          (c) => c.id !== conversationIdToDelete
        );
        queryClient.setQueryData(
          trpc.chat.conversations.fetchAll.queryKey(),
          newConversations
        );

        return { previousConversations, newConversations };
      },
      onSettled: async (data, variables, context) => {
        await queryClient.invalidateQueries({
          queryKey: trpc.chat.conversations.fetchAll.queryKey(),
        });
      },
      onError: async (error, variables, context) => {
        console.error(error);
        if (!context) return;
        queryClient.setQueryData(
          trpc.chat.conversations.fetchAll.queryKey(),
          context.previousConversations
        );
      },
    })
  );

  const { data: conversations } = useQuery(
    trpc.chat.conversations.fetchAll.queryOptions()
  );
  const selectedConversationId = useStore(
    (state) => state.selectedConversationId
  );

  async function handleDeleteConversation(conversationIdToDelete: string) {
    await deleteConversation.mutateAsync(
      { id: conversationIdToDelete },
      {
        onSuccess: async (x, y, { newConversations }) => {
          /** If the selected conversation was deleted, navigate/select a
           * different conversation (creating a new one if necessary): */
          if (conversationIdToDelete === selectedConversationId) {
            if (newConversations && newConversations.length > 0) {
              // Navigate to the first conversation
              const lastConversation =
                newConversations[newConversations.length - 1];
              await navigate(`/chat/${lastConversation.id}`);
            } else {
              // No conversations left, create a new one
              const newConversation = await startConversation.mutateAsync();
              if (!newConversation?.id) return;
              await navigate(`/chat/${newConversation.id}`);
            }
          }
        },
      }
    );
  }

  return (
    <NavLink
      key="chat-new"
      href="#required-for-focus-management"
      label={
        <Group justify="space-between">
          <span>Chats</span>
          <IconPlus
            size={16}
            stroke={1.5}
            className="border-on-hover"
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              startConversation.mutateAsync().then((res) => {
                if (!res?.id) return;
                // addConversation(res);
                navigate(`/chat/${res.id}`);
              });
            }}
          />
        </Group>
      }
      leftSection={<IconActivity size={16} stroke={1.5} />}
      rightSection={
        <IconChevronRight
          size={12}
          stroke={1.5}
          className="mantine-rotate-rtl"
        />
      }
      variant="subtle"
      active={urlPathname.startsWith("/chat")}
      defaultOpened={true}
    >
      {conversations?.map((conversation) => (
        <NavLink
          key={conversation.id}
          href={`/chat/${conversation.id}`}
          label={conversation.title}
          className="hover-container"
          leftSection={
            <>
              <IconCircle size={16} stroke={1.5} className="show-by-default" />
              <IconCircleFilled
                size={16}
                stroke={1.5}
                className="show-on-hover"
              />
            </>
          }
          rightSection={
            <>
              <IconTrash
                size={16}
                stroke={1.5}
                onClick={(e) => {
                  e.stopPropagation();
                  e.preventDefault();
                  handleDeleteConversation(conversation.id);
                }}
                className="show-by-default"
              />
              <IconTrashFilled
                size={16}
                stroke={1.5}
                onClick={(e) => {
                  e.stopPropagation();
                  e.preventDefault();
                  handleDeleteConversation(conversation.id);
                }}
                className="show-on-hover border-on-hover"
              />
            </>
          }
          variant="subtle"
          active={conversation.id === selectedConversationId}
        />
      ))}
    </NavLink>
  );
}
