'use client';

/**
 * Next.js adapter — bridges our framework-agnostic router hooks
 * to `next/navigation` so server components, route loaders, and
 * `prefetch` all fire correctly.
 *
 * USAGE:
 *   Mount once near the root of the App Router tree, e.g. inside
 *   the locale layout's client provider stack:
 *
 *   ```tsx
 *   import { NextRouterAdapter } from '@djangocfg/ui-core/adapters/nextjs';
 *
 *   <I18nProvider locale={locale} messages={messages}>
 *     <NextRouterAdapter>
 *       <AppLayout>{children}</AppLayout>
 *     </NextRouterAdapter>
 *   </I18nProvider>
 *   ```
 *
 *   Without this provider, our hooks fall back to the History API
 *   adapter — which works in plain SPAs (Wails, Electron, Vite, CRA)
 *   but in Next.js means: no server component refetch on navigation,
 *   no route loader, no prefetch. So always mount it in Next apps.
 *
 * PEER DEPENDENCY:
 *   `next` is an OPTIONAL peer of @djangocfg/ui-core. The base package
 *   never imports from `next/*`. This sub-path entry does — so it only
 *   loads when the consumer explicitly imports `/adapters/nextjs`.
 *   Wails / Electron consumers: don't import this file and `next` is
 *   never resolved.
 */

import {
  forwardRef,
  useMemo,
  type AnchorHTMLAttributes,
  type ComponentType,
  type ReactNode,
  type Ref,
} from 'react';
// Use the toploader-wrapped router so that EVERY programmatic
// navigation through ui-core hooks (`useNavigate`, `useQueryState`,
// `useRouter` from `@djangocfg/ui-core/hooks`) starts the
// `nextjs-toploader` progress bar.
//
// Why not `next/navigation` directly: <NextTopLoader> only attaches a
// document-level click listener for <a> tags and instruments
// `history.pushState` to call `nprogress.done()` (NOT `.start()`). So
// `router.push()` from `next/navigation` mutates history but never
// starts the bar — only `<Link>` clicks do. The `nextjs-toploader/app`
// entry exposes a thin wrapper around the same `useRouter` whose
// `push`/`replace` call `nprogress.start()` first. Drop-in shape.
import { useRouter as useNextRouter } from 'nextjs-toploader/app';
import NextLink from 'next/link';

import {
  RouterAdapterProvider,
  type RouterAdapter,
  type RouterLocation,
} from '../adapter';
import {
  LinkProvider,
  type LinkComponent,
  type LinkComponentProps,
} from '../../../components/navigation/link';

const SSR_LOCATION: RouterLocation = Object.freeze({
  pathname: '/',
  search: '',
  hash: '',
});

export interface NextRouterAdapterProps {
  children: ReactNode;
  /**
   * Pass `scroll: false` to every Next router call by default.
   * Useful for filter/pagination apps where the page should never
   * jump on URL changes. Default: undefined (use Next's own default).
   */
  defaultScroll?: boolean;
}

/**
 * Wraps a subtree so all `@djangocfg/ui-core/hooks` router calls go
 * through Next's App Router. Server components, loaders, and prefetch
 * keep working as if you used `next/navigation` directly.
 */
export function NextRouterAdapter({
  children,
  defaultScroll,
}: NextRouterAdapterProps) {
  const next = useNextRouter();

  const adapter = useMemo<RouterAdapter>(
    () => ({
      push(url) {
        next.push(url, defaultScroll === undefined ? undefined : { scroll: defaultScroll });
      },
      replace(url) {
        next.replace(url, defaultScroll === undefined ? undefined : { scroll: defaultScroll });
      },
      back() {
        next.back();
      },
      forward() {
        next.forward();
      },
      getLocation() {
        if (typeof window === 'undefined') return SSR_LOCATION;
        return {
          pathname: window.location.pathname,
          search: window.location.search,
          hash: window.location.hash,
        };
      },
    }),
    [next, defaultScroll]
  );

  return (
    <RouterAdapterProvider value={adapter}>{children}</RouterAdapterProvider>
  );
}

/**
 * Maps our agnostic Link API → next/link props. Lives at module scope so
 * the component identity is stable (avoids tree remounts on every render).
 */
const NextLinkAdapter: LinkComponent = forwardRef<HTMLAnchorElement, LinkComponentProps>(
  function NextLinkAdapter({ href, replace, scroll, prefetch, children, ...rest }, ref) {
    return (
      <NextLink
        href={href}
        replace={replace}
        scroll={scroll}
        prefetch={prefetch ?? undefined}
        ref={ref}
        {...rest}
      >
        {children}
      </NextLink>
    );
  }
);

export interface NextLinkProviderProps {
  children: ReactNode;
}

/**
 * Wires `<Link>` from `@djangocfg/ui-core/components` to `next/link`.
 * Mount alongside `NextRouterAdapter` near the root of a Next app so
 * every Link picks up Next's prefetch / RSC handling automatically.
 */
export function NextLinkProvider({ children }: NextLinkProviderProps) {
  return <LinkProvider value={NextLinkAdapter}>{children}</LinkProvider>;
}

/**
 * Minimal shape of the `Link` returned by next-intl's `createNavigation()`.
 * Typed structurally so we don't take a peer dep on `next-intl`.
 */
type NextIntlLinkLike = ComponentType<
  AnchorHTMLAttributes<HTMLAnchorElement> & {
    href: string;
    locale?: string;
    replace?: boolean;
    scroll?: boolean;
    prefetch?: boolean | null;
    ref?: Ref<HTMLAnchorElement>;
  }
>;

/**
 * Wraps next-intl's `Link` (from `createNavigation()`) into our generic
 * `LinkComponent`, so apps using `next-intl` get locale-prefixed hrefs on
 * every `<Link>` rendered through `@djangocfg/ui-core`.
 *
 * Pass the result as `baseApp.linkAdapter` to `AppLayout` / `BaseApp` —
 * it overrides the default `next/link` adapter for the whole subtree.
 *
 * @example
 *   import { createNextIntlLinkAdapter } from '@djangocfg/ui-core/adapters/nextjs';
 *   import { Link as IntlLink } from '@/i18n/navigation';
 *
 *   const linkAdapter = useMemo(() => createNextIntlLinkAdapter(IntlLink), []);
 *   <AppLayout baseApp={{ ..., linkAdapter }} />
 */
export function createNextIntlLinkAdapter(
  NextIntlLink: NextIntlLinkLike,
): LinkComponent {
  return forwardRef<HTMLAnchorElement, LinkComponentProps>(
    function NextIntlLinkAdapter({ href, replace, scroll, prefetch, children, ...rest }, ref) {
      return (
        <NextIntlLink
          href={href}
          replace={replace}
          scroll={scroll}
          prefetch={prefetch}
          ref={ref}
          {...rest}
        >
          {children}
        </NextIntlLink>
      );
    },
  );
}
