"use client";

import { useCallback, useMemo } from "react";
import { useRouter } from "next/navigation";

import { NavigationStorage } from "@/data/storage/navigation";
import type { SubscriptionReturnTo } from "@/lib/navigation/subscription_exit";
import {
  consumeSubscriptionExitUrl,
  peekSubscriptionExplicitExitUrl,
} from "@/lib/navigation/subscription_exit";
import { getDefaultPayChannelForCountryCode } from "@/lib/payment/default_pay_channel";
import { useAuthState } from "@/stores/auth/auth-context";
import { useUserState } from "@/stores/user/user-context";

import {
  ROUTE_BUILDERS,
  ROUTES,
  type Route,
} from "./routes";
import {
  isAuthenticatedUser as resolveIsAuthenticatedUser,
  resolveAuthenticatedNavigation,
} from "./navigation-resolver";
import type {
  OpenSubscriptionForPendingUnlockInput,
  OpenSubscriptionInput,
  StartMessageUnlockInput,
} from "./navigation-types";

export interface AppNavigator {
  push: (href: string, options?: { scroll?: boolean }) => void;
  replace: (href: string, options?: { scroll?: boolean }) => void;
  back: () => void;
  openAuth: (redirectTo?: string) => void;
  openChat: (options?: { replace?: boolean; scroll?: boolean }) => void;
  openSubscription: (input: OpenSubscriptionInput) => void;
  exitSubscription: (returnTo: SubscriptionReturnTo) => void;
  exitSubscriptionAfterSuccess: (returnTo: SubscriptionReturnTo) => void;
  startMessageUnlock: (input: StartMessageUnlockInput) => void;
  openSubscriptionForPendingUnlock: (
    input: OpenSubscriptionForPendingUnlockInput,
  ) => void;
  getDefaultPayChannel: () => ReturnType<typeof getDefaultPayChannelForCountryCode>;
  isAuthenticatedUser: boolean;
}

export function useAppNavigator(): AppNavigator {
  const router = useRouter();
  const authState = useAuthState();
  const userState = useUserState();
  const isAuthenticatedUser = resolveIsAuthenticatedUser(authState.loginStatus);

  const push = useCallback((href: string, options?: { scroll?: boolean }): void => {
    router.push(href, options);
  }, [router]);

  const replace = useCallback((href: string, options?: { scroll?: boolean }): void => {
    router.replace(href, options);
  }, [router]);

  const back = useCallback((): void => {
    router.back();
  }, [router]);

  const openAuth = useCallback((redirectTo: string = ROUTES.chat): void => {
    router.push(ROUTE_BUILDERS.authWithRedirect(redirectTo));
  }, [router]);

  const openChat = useCallback((options: { replace?: boolean; scroll?: boolean } = {}): void => {
    const navOptions = { scroll: options.scroll ?? false };
    if (options.replace) {
      router.replace(ROUTES.chat, navOptions);
      return;
    }
    router.push(ROUTES.chat, navOptions);
  }, [router]);

  const getDefaultPayChannel = useCallback(() => {
    return getDefaultPayChannelForCountryCode(userState.currentUser?.countryCode);
  }, [userState.currentUser?.countryCode]);

  const openSubscription = useCallback(
    ({
      type,
      payChannel = getDefaultPayChannel(),
      returnTo = null,
      replace: shouldReplace = false,
    }: OpenSubscriptionInput): void => {
      const target = ROUTE_BUILDERS.subscription(type, {
        payChannel,
        returnTo: returnTo ?? undefined,
      });

      const nextUrl = resolveAuthenticatedNavigation({
        loginStatus: authState.loginStatus,
        targetUrl: target,
      });

      if (shouldReplace) {
        router.replace(nextUrl);
        return;
      }
      router.push(nextUrl);
    },
    [authState.loginStatus, getDefaultPayChannel, router],
  );

  const startMessageUnlock = useCallback(
    ({
      messageId,
      kind,
      returnUrl,
      stage = "auth",
      onAuthenticated,
    }: StartMessageUnlockInput): void => {
      if (isAuthenticatedUser) {
        onAuthenticated();
        return;
      }

      void (async () => {
        await NavigationStorage.savePendingChatUnlock({
          messageId,
          kind,
          returnUrl,
          stage,
        });
        router.push(ROUTE_BUILDERS.authWithRedirect(returnUrl));
      })();
    },
    [isAuthenticatedUser, router],
  );

  const openSubscriptionForPendingUnlock = useCallback(
    ({
      messageId,
      kind,
      returnUrl,
      type,
      payChannel = getDefaultPayChannel(),
    }: OpenSubscriptionForPendingUnlockInput): void => {
      void (async () => {
        await NavigationStorage.savePendingChatUnlock({
          messageId,
          kind,
          returnUrl,
          stage: "payment",
        });
        openSubscription({
          type,
          payChannel,
          returnTo: "chat",
        });
      })();
    },
    [getDefaultPayChannel, openSubscription],
  );

  const exitSubscription = useCallback((returnTo: SubscriptionReturnTo): void => {
    void (async () => {
      router.replace(await consumeSubscriptionExitUrl(returnTo));
    })();
  }, [router]);

  const exitSubscriptionAfterSuccess = useCallback((returnTo: SubscriptionReturnTo): void => {
    void (async () => {
      const pendingExitUrl = await peekSubscriptionExplicitExitUrl();
      if (pendingExitUrl) {
        router.replace(pendingExitUrl);
        return;
      }
      router.replace(await consumeSubscriptionExitUrl(returnTo));
    })();
  }, [router]);

  return useMemo(() => ({
    push,
    replace,
    back,
    openAuth,
    openChat,
    openSubscription,
    exitSubscription,
    exitSubscriptionAfterSuccess,
    startMessageUnlock,
    openSubscriptionForPendingUnlock,
    getDefaultPayChannel,
    isAuthenticatedUser,
  }), [
    back,
    exitSubscription,
    exitSubscriptionAfterSuccess,
    getDefaultPayChannel,
    isAuthenticatedUser,
    openAuth,
    openChat,
    openSubscription,
    openSubscriptionForPendingUnlock,
    push,
    replace,
    startMessageUnlock,
  ]);
}

export type { Route };
