'use client'

import {
  type HTMLAttributes,
  type MutableRefObject,
  type Ref,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react'
import {
  DEFAULT_HEADER_FOOTER_URL,
  type IHeaderMenuButton,
  type IHeaderMenuLink,
  type IHeaderMenuSection,
  type IHeaderMenuServices,
  type IPktHeaderMenu as ISharedPktHeaderMenu,
  type THeaderFooterApi,
  type THeaderMenuLocale,
} from 'shared-types'
import { deriveSocialIcon, fetchHeaderFooterData, mapOdsIcon, selectLocaleData } from 'shared-utils/header-footer'

import { useElementWidth } from '../../hooks/useElementWidth'
import { PktAccordion } from '../accordion/Accordion'
import { PktAccordionItem } from '../accordion/AccordionItem'
import { PktIcon } from '../icon/Icon'

export type {
  THeaderFooterApi,
  THeaderMenuLocale,
  IHeaderMenuLink,
  IHeaderMenuButton,
  IHeaderMenuSection,
  IHeaderMenuServices,
}

export interface IPktHeaderMenu extends Omit<HTMLAttributes<HTMLElement>, 'onError'>, ISharedPktHeaderMenu {
  /** Forwarded to the host element. */
  ref?: Ref<HTMLElement>
  /** Fired when the payload has been fetched (or `data` was supplied). */
  onDataLoaded?: (data: THeaderFooterApi) => void
  /** Fired when the fetch fails. */
  onDataError?: (error: Error) => void
}

type LoadState = 'idle' | 'loading' | 'ready' | 'error'

/**
 * `<PktHeaderMenu>` — global mega menu for Oslo kommune.
 *
 * Fetches the live header/footer payload on mount and renders the
 * `megamenu` slice for the current locale. Mirrors `pkt-header-menu`
 * (Punkt Elements) — both implementations share types and data
 * helpers from `shared-utils/header-footer`.
 *
 * This component is purely presentational with respect to focus/scroll-lock;
 * the parent header is expected to control `open` and own focus
 * management.
 */
export const PktHeaderMenu = ({
  dataUrl = DEFAULT_HEADER_FOOTER_URL,
  data,
  locale = 'nb-NO',
  open = false,
  mobileBreakpoint = 768,
  className,
  onDataLoaded,
  onDataError,
  ref,
  ...rest
}: IPktHeaderMenu) => {
  const [fetchState, setFetchState] = useState<LoadState>('loading')
  const [fetchedData, setFetchedData] = useState<THeaderFooterApi | undefined>(undefined)
  const loadState: LoadState = data ? 'ready' : fetchState

  const onDataLoadedRef = useRef(onDataLoaded)
  const onDataErrorRef = useRef(onDataError)
  useEffect(() => {
    onDataLoadedRef.current = onDataLoaded
  }, [onDataLoaded])
  useEffect(() => {
    onDataErrorRef.current = onDataError
  }, [onDataError])

  const rootRef = useRef<HTMLElement | null>(null)
  const setRootRef = useCallback(
    (element: HTMLElement | null) => {
      rootRef.current = element
      if (typeof ref === 'function') ref(element)
      else if (ref) (ref as MutableRefObject<HTMLElement | null>).current = element
    },
    [ref],
  )
  const menuWidth = useElementWidth(rootRef)
  const isMobile = menuWidth < mobileBreakpoint

  useEffect(() => {
    if (data) onDataLoadedRef.current?.(data)
  }, [data])

  // Fetch when no `data` prop is supplied
  useEffect(() => {
    if (data) return
    const controller = new AbortController()

    fetchHeaderFooterData<string>(dataUrl, controller.signal)
      .then((payload) => {
        if (controller.signal.aborted) return
        setFetchedData(payload)
        setFetchState('ready')
        onDataLoadedRef.current?.(payload)
      })
      .catch((error: Error) => {
        if (error.name === 'AbortError') return
        setFetchState('error')
        onDataErrorRef.current?.(error)
      })

    return () => controller.abort()
  }, [data, dataUrl])

  const effectiveData = data ?? fetchedData
  const localeData = useMemo(() => selectLocaleData(effectiveData, locale), [effectiveData, locale])

  const hostClasses = ['pkt-header-menu', open && 'pkt-header-menu--open', className].filter(Boolean).join(' ')

  if (loadState === 'loading') {
    return (
      <nav {...rest} ref={setRootRef} className={hostClasses} aria-busy="true">
        <p className="pkt-header-menu__loading">Laster meny…</p>
      </nav>
    )
  }

  if (loadState === 'error' || !localeData) {
    return (
      <nav {...rest} ref={setRootRef} className={hostClasses} aria-hidden={!open}>
        <p className="pkt-header-menu__error">Kunne ikke laste meny.</p>
      </nav>
    )
  }

  const { megamenu, i18n } = localeData
  const navAriaLabel = i18n?.navAriaLabel || 'Hovedmeny'

  return (
    <nav {...rest} ref={setRootRef} className={hostClasses} aria-label={navAriaLabel}>
      {isMobile ? (
        <MobileAccordion services={megamenu.services} sections={megamenu.sections} />
      ) : (
        <>
          <Services services={megamenu.services} />
          <Buttons buttons={megamenu.buttons} mobile={false} />
          <Sections sections={megamenu.sections} />
        </>
      )}
      <Buttons buttons={megamenu.buttons} mobile={true} />
      <Footer links={megamenu.links} some={megamenu.some} />
    </nav>
  )
}

const ServicesList = ({ services }: { services: IHeaderMenuServices }) => (
  <ul className="pkt-header-menu__services-list">
    {services.links.map((link) => (
      <li className="pkt-header-menu__service" key={link.url}>
        <a className="pkt-header-menu__service-link" href={link.url}>
          <PktIcon className="pkt-header-menu__service-icon" name={mapOdsIcon(link.icon)} aria-hidden="true" />
          <span className="pkt-header-menu__service-text">{link.text}</span>
        </a>
      </li>
    ))}
  </ul>
)

const MobileAccordion = ({ services, sections }: { services: IHeaderMenuServices; sections: IHeaderMenuSection[] }) => (
  <div className="pkt-header-menu__sections">
    <PktAccordion className="pkt-header-menu__sections-inner" skin="plus-minus" name="header-menu-accordion">
      <PktAccordionItem
        className="pkt-header-menu__section"
        skin="plus-minus"
        id="pkt-header-menu-services"
        title={services.title}
      >
        <ServicesList services={services} />
      </PktAccordionItem>
      {sections.map((section, index) => (
        <PktAccordionItem
          key={section.title}
          className="pkt-header-menu__section"
          skin="plus-minus"
          id={`pkt-header-menu-section-${index}`}
          title={section.title}
        >
          <SectionList links={section.links} />
        </PktAccordionItem>
      ))}
    </PktAccordion>
  </div>
)

const Services = ({ services }: { services: IHeaderMenuServices }) => (
  <div className="pkt-header-menu__services">
    <h2 className="pkt-header-menu__services-title">{services.title}</h2>
    <ServicesList services={services} />
  </div>
)

const Buttons = ({ buttons, mobile }: { buttons: IHeaderMenuButton[] | undefined; mobile: boolean }) => {
  if (!buttons || buttons.length === 0) return null
  const classes = ['pkt-header-menu__buttons', mobile && 'pkt-header-menu__buttons--mobile'].filter(Boolean).join(' ')
  return (
    <div className={classes}>
      {buttons.map((button) => (
        <a key={button.url} className="pkt-btn pkt-btn--secondary pkt-btn--icon-right pkt-btn--small" href={button.url}>
          <PktIcon
            className="pkt-btn__icon"
            name={button.iconName ? mapOdsIcon(button.iconName) : 'user'}
            aria-hidden="true"
          />
          <span className="pkt-btn__text">{button.text}</span>
        </a>
      ))}
    </div>
  )
}

const Sections = ({ sections }: { sections: IHeaderMenuSection[] }) => {
  if (!sections || sections.length === 0) return null

  return (
    <div className="pkt-header-menu__sections">
      <div className="pkt-header-menu__sections-inner">
        {sections.map((section) => (
          <div className="pkt-header-menu__section" key={section.title}>
            <h2 className="pkt-header-menu__section-title">{section.title}</h2>
            <SectionList links={section.links} />
          </div>
        ))}
      </div>
    </div>
  )
}

const SectionList = ({ links }: { links: IHeaderMenuLink[] }) => (
  <ul className="pkt-header-menu__section-list">
    {links.map((link) => (
      <li key={link.url}>
        <a className="pkt-header-menu__section-link" href={link.url}>
          {link.text}
        </a>
      </li>
    ))}
  </ul>
)

const Footer = ({ links, some }: { links: IHeaderMenuLink[]; some: IHeaderMenuLink[] }) => {
  const hasLinks = links && links.length > 0
  const hasSome = some && some.length > 0
  if (!hasLinks && !hasSome) return null

  return (
    <div className="pkt-header-menu__footer">
      {hasLinks && (
        <ul className="pkt-header-menu__footer-list">
          {links.map((link) => (
            <li key={link.url}>
              <a className="pkt-header-menu__footer-link" href={link.url}>
                {link.text}
              </a>
            </li>
          ))}
        </ul>
      )}
      {hasSome && (
        <ul className="pkt-header-menu__footer-list pkt-header-menu__footer-list--social">
          {some.map((entry) => (
            <SocialLink key={entry.url} entry={entry} />
          ))}
        </ul>
      )}
    </div>
  )
}

const SocialLink = ({ entry }: { entry: IHeaderMenuLink }) => {
  const iconName = deriveSocialIcon(entry.url, entry.text)
  return (
    <li>
      <a className="pkt-header-menu__social-link" href={entry.url} aria-label={entry.text}>
        {iconName ? <PktIcon name={iconName} aria-hidden="true" /> : <span>{entry.text}</span>}
      </a>
    </li>
  )
}
