"use client";

import type { SocialProvider } from "aau-auth-kit";
import { type ReactNode, createContext, useMemo } from "react";
import { toast } from "sonner";

import { useAuthData } from "../hooks/use-auth-data";
import type { AdditionalFields } from "../types/additional-fields";
// import type { AnyAuthClient } from "../types/any-auth-client";
import type { AuthClient } from "../types/auth-client";
import type { AuthHooks } from "../types/auth-hooks";
import type { AuthMutators } from "../types/auth-mutators";
import type { Link } from "../types/link";
import type { RenderToast } from "../types/render-toast";
import { type AuthViewPaths, authViewPaths } from "./auth-view-paths";

export type Role = {
  key: string;
  value: string;
};

const defaultRole = [
  {
    key: "owner",
    value: "Owner",
  },
  {
    key: "member",
    value: "Member",
  },
  {
    key: "admin",
    value: "Admin",
  },
];

const DefaultLink: Link = ({ href, className, children }) => (
  <a className={className} href={href}>
    {children}
  </a>
);

const defaultNavigate = (href: string) => {
  window.location.href = href;
};

const defaultPathname = () => window.location.pathname;

const defaultReplace = (href: string) => {
  window.location.replace(href);
};

const defaultToast: RenderToast = ({ variant = "default", message }) => {
  if (variant === "default") {
    toast(message);
  } else {
    toast[variant](message);
  }
};

export type AuthUIContextType = {
  authClient: AuthClient;
  adminRole?: string;
  roles: Role[];

  createOrganizationUrl?: string;

  /**
   * Additional fields for users
   */
  additionalFields?: AdditionalFields;
  /**
   * File extension for Avatar uploads
   * @default "png"
   */
  /**
   * Enable or disable Avatar support
   * @default false
   */
  avatar?: boolean;
  avatarExtension: string;
  /**
   * Avatars are resized to 128px unless uploadAvatar is provided, then 256px
   * @default 128 | 256
   */
  avatarSize: number;
  /**
   * Base path for the auth views
   * @default "/auth"
   */
  basePath: string;
  /**
   * Front end base URL for auth API callbacks
   */
  baseURL?: string;
  /**
   * Enable or disable the Confirm Password input
   * @default false
   */
  confirmPassword?: boolean;
  /**
   * Enable or disable Credentials support
   * @default true
   */
  credentials?: boolean;
  /**
   * Default redirect URL after authenticating
   * @default "/"
   */
  redirectTo: string;
  /**
   * Enable or disable email verification for account deletion
   * @default false
   */

  /**
   * Enable or disable User Account deletion
   * @default false
   */
  deleteUser?: boolean;
  /**
   * Show Verify Email card for unverified emails
   */
  emailVerification?: boolean;
  /**
   * Enable or disable Forgot Password flow
   * @default true
   */
  forgotPassword?: boolean;
  /**
   * Freshness age for Session data
   * @default 60 * 60 * 24
   */
  freshAge: number;
  /**
   * @internal
   */
  hooks: AuthHooks;
  /**
   * Enable or disable Email OTP support
   * @default false
   */
  emailOTP?: boolean;
  /** @internal */
  mutators: AuthMutators;
  /**
   * Enable or disable name requirement for Sign Up
   * @default true
   */
  nameRequired?: boolean;
  /**
   * Force black & white icons for both light and dark themes
   * @default false
   */
  noColorIcons?: boolean;
  /**
   * Enable or disable One Tap support
   * @default false
   */
  /**
   * Perform some User updates optimistically
   * @default false
   */
  optimistic?: boolean;
  /**
   * Array of Social Providers to enable
   * @remarks `SocialProvider[]`
   */
  providers?: SocialProvider[];
  /**
   * Enable or disable Remember Me checkbox
   * @default false
   */
  rememberMe?: boolean;
  /**
   * Array of fields to show in `<SettingsCards />`
   * @default ["name"]
   */
  settingsFields?: string[];
  /**
   * Custom Settings URL
   */
  settingsURL?: string;
  /**
   * Enable or disable Sign Up form
   * @default true
   */
  signUp?: boolean;
  /**
   * Enable or disable two-factor authentication support
   * @default undefined
   */
  twoFactor?: ("otp" | "totp")[];
  /**
   * Array of fields to show in Sign Up form
   * @default ["name"]
   */
  signUpFields?: string[];
  /**
   * Custom social sign in function
   */
  signInSocial?: (
    params: Parameters<AuthClient["signIn"]["social"]>[0]
  ) => Promise<unknown>;
  toast: RenderToast;
  viewPaths: AuthViewPaths;
  /**
   * Navigate to a new URL
   * @default window.location.href
   */
  navigate: typeof defaultNavigate;
  /**
   * Called whenever the Session changes
   */
  pathname?: () => string;
  onSessionChange?: () => void | Promise<void>;
  /**
   * Replace the current URL
   * @default navigate
   */
  replace: typeof defaultReplace;
  /**
   * Upload an Avatar image and return the URL string
   * @remarks `(file: File) => Promise<string>`
   */
  uploadAvatar?: (file: File) => Promise<string | undefined | null>;
  /**
   * Custom Link component for navigation
   * @default <a>
   */
  Link: Link;
  username?: boolean;

  /**
   * Enable phone number authentication
   * @default false
   */
  phoneNumber?: boolean;
  otpLength?: number;
};

export type AuthUIProviderProps = {
  children: ReactNode;
  /**
   * Better Auth client returned from createAuthClient
   * @default Required
   * @remarks `AuthClient`
   */
  authClient: AuthClient;
  /**
   * ADVANCED: Custom hooks for fetching auth data
   */
  hooks?: Partial<AuthHooks>;
  /**
   * Customize the paths for the auth views
   * @default authViewPaths
   * @remarks `AuthViewPaths`
   */
  viewPaths?: Partial<AuthViewPaths>;
  /**
   * Render custom Toasts
   * @default Sonner
   */
  toast?: RenderToast;
  roles?: Role[];
  /**
   * ADVANCED: Custom mutators for updating auth data
   */
  mutators?: Partial<AuthMutators>;
} & Partial<
  Omit<
    AuthUIContextType,
    "viewPaths" | "localization" | "mutators" | "toast" | "hooks" | "roles"
  >
>;

export const AuthUIContext = createContext<AuthUIContextType>(
  {} as unknown as AuthUIContextType
);

export const AuthUIProvider = ({
  children,
  authClient,
  avatarExtension = "png",
  avatarSize,
  basePath = "/auth",
  baseURL = "",
  redirectTo = "/",
  credentials = true,
  forgotPassword = true,
  freshAge = 60 * 60 * 24,
  hooks: hooksProp,
  mutators: mutatorsProp,
  nameRequired = true,
  settingsFields = ["name"],
  signUp = true,
  signUpFields = ["name"],
  toast = defaultToast,
  viewPaths: viewPathsProp,
  navigate,
  replace,
  uploadAvatar,
  Link = DefaultLink,
  pathname,
  adminRole,
  roles,
  ...props
}: AuthUIProviderProps) => {
  const defaultMutators = useMemo(() => {
    return {
      revokeSession: (params) =>
        authClient.revokeSession({
          ...params,
          fetchOptions: { throw: true },
        }),
      updateUser: (params) =>
        authClient.updateUser({
          ...params,
          fetchOptions: { throw: true },
        }),
    } as AuthMutators;
  }, [authClient]);

  const defaultHooks = useMemo(() => {
    return {
      useSession: authClient.useSession,
      useListAccounts: () => useAuthData({ queryFn: authClient.listAccounts }),
      useListSessions: () => useAuthData({ queryFn: authClient.listSessions }),
    } as AuthHooks;
  }, [authClient]);

  const viewPaths = useMemo(() => {
    return { ...authViewPaths, ...viewPathsProp } as AuthViewPaths;
  }, [viewPathsProp]);

  const hooks = useMemo(() => {
    return { ...defaultHooks, ...hooksProp } as AuthHooks;
  }, [defaultHooks, hooksProp]);

  const mutators = useMemo(() => {
    return { ...defaultMutators, ...mutatorsProp } as AuthMutators;
  }, [defaultMutators, mutatorsProp]);

  // Remove trailing slash from baseURL
  baseURL = baseURL.endsWith("/") ? baseURL.slice(0, -1) : baseURL;

  // Remove trailing slash from basePath
  basePath = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
  const admin = adminRole ?? "admin";
  const finalRoles = roles ?? defaultRole;

  return (
    <AuthUIContext.Provider
      value={{
        roles: finalRoles,
        authClient,
        avatarExtension,
        avatarSize: avatarSize || (uploadAvatar ? 256 : 128),
        basePath: basePath === "/" ? "" : basePath,
        baseURL,
        redirectTo,
        credentials,
        forgotPassword,
        freshAge,
        hooks,
        mutators,
        nameRequired,
        settingsFields,
        signUp,
        signUpFields,
        toast,
        navigate: navigate || defaultNavigate,
        pathname: pathname || defaultPathname,
        replace: replace || navigate || defaultReplace,
        viewPaths,
        uploadAvatar,
        Link,
        adminRole: admin,
        ...props,
      }}
    >
      {children}
    </AuthUIContext.Provider>
  );
};
