'use client'

import classNames from 'classnames'
import {
  type ChangeEvent,
  type FocusEvent,
  ForwardedRef,
  forwardRef,
  type KeyboardEvent,
  type MutableRefObject,
  type RefObject,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react'

import { useElementWidth } from '../../hooks/useElementWidth'
import { useScrollLock } from '../../hooks/useScrollLock'
import { PktButton } from '../button/Button'
import { PktHeaderUserMenu } from '../headerUserMenu/UserMenu'
import { PktIcon } from '../icon/Icon'
import { PktLink } from '../link/Link'
import { PktTextinput } from '../textinput/Textinput'
import { IPktHeader, Representing, THeaderMenu, User, UserMenuFooterItem, UserMenuItem } from './types'

export type { Representing, THeaderMenu, User, UserMenuFooterItem, UserMenuItem }
export type IPktHeaderService = IPktHeader

export const PktHeaderService = forwardRef(
  (
    {
      mobileBreakpoint = 768,
      tabletBreakpoint = 1280,
      children,
      className,
      compact = false,
      'data-mode': dataMode,
      logOutButtonPlacement = 'none',
      logOut,
      openedMenu,
      showSearch = false,
      onSearch,
      onSearchChange,
      searchValue,
      searchPlaceholder = 'Søk',
      representing,
      serviceLink,
      serviceClick,
      serviceName,
      user,
      userMenu,
      userMenuFooter,
      userOptions,
      canChangeRepresentation = false,
      changeRepresentation,
      hideLogo = false,
      logoLink,
      logoClick,
      position = 'fixed',
      scrollBehavior = 'hide',
      slotMenuVariant = 'icon-only',
      slotMenuText = 'Meny',
    }: IPktHeaderService,
    ref: ForwardedRef<HTMLDivElement>,
  ) => {
    const isFixed = position === 'fixed'
    const shouldHideOnScroll = scrollBehavior === 'hide'

    // Deprecation warning for userMenuFooter
    useEffect(() => {
      if (userMenuFooter) {
        // eslint-disable-next-line no-console
        console.warn(
          'PktHeaderService: The "userMenuFooter" prop is deprecated and will be removed in a future version. Please use "userMenu" instead.',
        )
      }
    }, [userMenuFooter])

    // Warning for userOptions - no longer available
    useEffect(() => {
      if (userOptions) {
        // eslint-disable-next-line no-console
        console.warn('PktHeaderService: The "userOptions" prop is no longer available. Please use "userMenu" instead.')
      }
    }, [userOptions])

    // Deprecation warning for shortname
    useEffect(() => {
      if (user?.shortname) {
        // eslint-disable-next-line no-console
        console.warn(
          'PktHeaderService: The "shortname" property on the user object is deprecated and will be removed in a future version.',
        )
      }
      if (representing?.shortname) {
        // eslint-disable-next-line no-console
        console.warn(
          'PktHeaderService: The "shortname" property on the representing object is deprecated and will be removed in a future version.',
        )
      }
    }, [user?.shortname, representing?.shortname])

    const formattedLastLoggedIn = useMemo(() => {
      if (!user?.lastLoggedIn) return undefined
      if (typeof user.lastLoggedIn === 'string') {
        return user.lastLoggedIn
      }
      return new Date(user.lastLoggedIn).toLocaleString('nb-NO', {
        year: 'numeric',
        month: 'long',
        day: 'numeric',
      })
    }, [user?.lastLoggedIn])

    const [openMenu, setOpenMenu] = useState<'none' | 'slot' | 'search' | 'user'>(openedMenu || 'none')

    useEffect(() => {
      if (openedMenu !== undefined) {
        setOpenMenu(openedMenu)
      }
    }, [openedMenu])

    const [hidden, setHidden] = useState(false)
    const [lastScrollPosition, setLastScrollPosition] = useState(0)
    const [alignSlotRight, setAlignSlotRight] = useState(false)
    const [alignSearchRight, setAlignSearchRight] = useState(false)

    const internalRef = useRef<HTMLDivElement>(null)
    const userContainerRef = useRef<HTMLDivElement>(null)
    const slotContainerRef = useRef<HTMLDivElement>(null)
    const searchContainerRef = useRef<HTMLDivElement>(null)
    const slotContentRef = useRef<HTMLDivElement>(null)
    const searchMenuRef = useRef<HTMLDivElement>(null)
    const lastFocusedElementRef = useRef<HTMLElement | null>(null)
    const shouldRestoreFocusRef = useRef(false)

    const headerWidth = useElementWidth(internalRef)
    const isMobile: boolean = headerWidth < mobileBreakpoint
    const isTablet: boolean = headerWidth < tabletBreakpoint

    const setRefs = useCallback(
      (element: HTMLDivElement | null) => {
        ;(internalRef as MutableRefObject<HTMLDivElement | null>).current = element
        if (typeof ref === 'function') {
          ref(element)
        } else if (ref) {
          ;(ref as MutableRefObject<HTMLDivElement | null>).current = element
        }
      },
      [ref],
    )

    useScrollLock(isFixed && isMobile && openMenu !== 'none')

    const handleFocusOut = useCallback((event: FocusEvent<HTMLDivElement>, menuType: THeaderMenu) => {
      const relatedTarget = event.relatedTarget as HTMLElement | null

      let containerRef: RefObject<HTMLDivElement>
      switch (menuType) {
        case 'user':
          containerRef = userContainerRef
          break
        case 'slot':
          containerRef = slotContainerRef
          break
        case 'search':
          containerRef = searchContainerRef
          break
        default:
          return
      }

      const container = containerRef.current
      if (!container) return

      if (!relatedTarget || !container.contains(relatedTarget)) {
        setOpenMenu('none')
      }
    }, [])

    const restoreFocus = useCallback(() => {
      if (
        shouldRestoreFocusRef.current &&
        lastFocusedElementRef.current &&
        document.contains(lastFocusedElementRef.current)
      ) {
        lastFocusedElementRef.current.focus()
      }
      lastFocusedElementRef.current = null
      shouldRestoreFocusRef.current = false
    }, [])

    useEffect(() => {
      const handleClickOutside = (event: MouseEvent) => {
        if (user && openMenu === 'user' && !(event.target as Element).closest('.pkt-header-service__user-container')) {
          setOpenMenu('none')
        }
        if (openMenu === 'slot' && !(event.target as Element).closest('.pkt-header-service__slot-container')) {
          setOpenMenu('none')
        }
        if (
          openMenu === 'search' &&
          !(event.target as Element).closest('.pkt-header-service__search-container') &&
          !(event.target as Element).closest('.pkt-header-service__search-input')
        ) {
          setOpenMenu('none')
        }
      }

      const handleEscapeKey = (event: globalThis.KeyboardEvent) => {
        if (event.key === 'Escape' && openMenu !== 'none') {
          event.preventDefault()
          shouldRestoreFocusRef.current = true
          setOpenMenu('none')
        }
      }

      if (openMenu !== 'none') {
        document.addEventListener('mousedown', handleClickOutside)
        document.addEventListener('keydown', handleEscapeKey)
        return () => {
          document.removeEventListener('mousedown', handleClickOutside)
          document.removeEventListener('keydown', handleEscapeKey)
        }
      } else {
        restoreFocus()
      }
    }, [openMenu, user, restoreFocus])

    useEffect(() => {
      const onScroll = () => {
        if (shouldHideOnScroll) {
          const currentScrollPosition = window.pageYOffset || document.documentElement.scrollTop
          if (currentScrollPosition < 0) {
            return
          }
          if (Math.abs(currentScrollPosition - lastScrollPosition) < 60) {
            return
          }
          setHidden(currentScrollPosition > lastScrollPosition)
          setLastScrollPosition(currentScrollPosition)
        }
      }

      if (document) {
        window.addEventListener('scroll', onScroll)
      }
      return () => {
        if (document) {
          window.removeEventListener('scroll', onScroll)
        }
      }
    }, [shouldHideOnScroll, lastScrollPosition])

    const checkDropdownAlignment = useCallback(
      (mode: 'slot' | 'search') => {
        const containerRef = mode === 'slot' ? slotContainerRef : searchContainerRef
        const dropdownRef = mode === 'slot' ? slotContentRef : searchMenuRef
        if (!containerRef.current || !dropdownRef.current || !isTablet || isMobile) return

        const buttonRect = containerRef.current.getBoundingClientRect()
        const dropdownWidth = dropdownRef.current.offsetWidth
        const wouldOverflow = buttonRect.left + dropdownWidth > window.innerWidth

        if (mode === 'slot') {
          setAlignSlotRight(wouldOverflow)
        } else {
          setAlignSearchRight(wouldOverflow)
        }
      },
      [isTablet, isMobile],
    )

    const handleMenuToggle = useCallback(
      (mode: THeaderMenu) => {
        if (openMenu !== 'none') {
          setOpenMenu('none')
        } else {
          lastFocusedElementRef.current = document.activeElement as HTMLElement

          setOpenMenu(mode)
        }
      },
      [openMenu],
    )

    useEffect(() => {
      if (openMenu === 'slot' || openMenu === 'search') {
        requestAnimationFrame(() => {
          checkDropdownAlignment(openMenu)
        })
      }
    }, [openMenu, checkDropdownAlignment])

    const headerElement = (
      <header
        className={classNames(
          `pkt-header-service`,
          compact && 'pkt-header-service--compact',
          isMobile && 'pkt-header-service--mobile',
          isTablet && 'pkt-header-service--tablet',
          isFixed && 'pkt-header-service--fixed',
          shouldHideOnScroll && 'pkt-header-service--scroll-to-hide',
          hidden && 'pkt-header-service--hidden',
          className,
        )}
        data-mode={dataMode}
        ref={setRefs}
      >
        <div
          className={classNames(
            'pkt-header-service__logo-area',
            (!serviceName || serviceName === '') && 'pkt-header-service__logo-area--without-service',
          )}
        >
          {!hideLogo && (
            <span className="pkt-header-service__logo">
              {logoLink ? (
                <a href={logoLink} aria-label="Tilbake til forside">
                  <PktIcon
                    name="oslologo"
                    aria-hidden="true"
                    path="https://punkt-cdn.oslo.kommune.no/latest/logos/"
                  ></PktIcon>
                </a>
              ) : logoClick ? (
                <button
                  aria-label="Tilbake til forside"
                  className="pkt-link-button pkt-link pkt-header-service__logo-link"
                  onClick={logoClick}
                >
                  <PktIcon
                    name="oslologo"
                    aria-hidden="true"
                    path="https://punkt-cdn.oslo.kommune.no/latest/logos/"
                  ></PktIcon>
                </button>
              ) : (
                <PktIcon
                  name="oslologo"
                  aria-hidden="true"
                  path="https://punkt-cdn.oslo.kommune.no/latest/logos/"
                ></PktIcon>
              )}
            </span>
          )}
          {serviceName && (
            <span className="pkt-header-service__service-name">
              {serviceLink ? (
                <PktLink href={serviceLink} className="pkt-header-service__service-link ">
                  {serviceName}
                </PktLink>
              ) : serviceClick ? (
                <button className="pkt-link-button pkt-link pkt-header-service__service-link" onClick={serviceClick}>
                  {serviceName}
                </button>
              ) : (
                <span className="pkt-header-service__service-link">{serviceName}</span>
              )}
            </span>
          )}
        </div>

        {!!children ||
        showSearch ||
        (isTablet && logOut && (logOutButtonPlacement === 'header' || logOutButtonPlacement === 'both')) ? (
          <div className="pkt-header-service__content">
            <div
              className={classNames('pkt-header-service__slot-container', openMenu === 'slot' && 'is-open')}
              onBlur={(e) => handleFocusOut(e, 'slot')}
              ref={slotContainerRef}
            >
              {isTablet && children && (
                <PktButton
                  skin="secondary"
                  variant={slotMenuVariant}
                  iconName="menu"
                  size={isMobile ? 'small' : 'medium'}
                  state={openMenu === 'slot' ? 'active' : 'normal'}
                  onClick={() => handleMenuToggle('slot')}
                  aria-expanded={openMenu === 'slot'}
                  aria-controls="mobile-slot-menu"
                  aria-label="Åpne meny"
                >
                  {slotMenuText}
                </PktButton>
              )}
              <div
                className={classNames('pkt-header-service__slot-content', alignSlotRight && 'align-right')}
                id="mobile-slot-menu"
                role={isTablet ? 'menu' : undefined}
                aria-label={isTablet ? 'Meny' : undefined}
                ref={slotContentRef}
              >
                <div className="pkt-contents">{children}</div>
              </div>
            </div>

            {showSearch && (
              <>
                {isTablet ? (
                  <div
                    className={classNames('pkt-header-service__search-container', openMenu === 'search' && 'is-open')}
                    onBlur={(e) => handleFocusOut(e, 'search')}
                    ref={searchContainerRef}
                  >
                    <PktButton
                      skin="secondary"
                      variant="icon-only"
                      iconName="magnifying-glass-big"
                      size={isMobile ? 'small' : 'medium'}
                      onClick={() => handleMenuToggle('search')}
                      state={openMenu === 'search' ? 'active' : 'normal'}
                      aria-expanded={openMenu === 'search'}
                      aria-controls="mobile-search-menu"
                      aria-label="Åpne søkefelt"
                    >
                      Søk
                    </PktButton>
                    <div
                      className={classNames(
                        'pkt-header-service__mobile-menu',
                        openMenu === 'search' && 'is-open',
                        alignSearchRight && 'align-right',
                      )}
                      ref={searchMenuRef}
                    >
                      {openMenu === 'search' && (
                        <PktTextinput
                          id="mobile-search-menu"
                          className="pkt-header-service__search-input"
                          type="search"
                          label="Søk"
                          useWrapper={false}
                          placeholder={searchPlaceholder}
                          value={searchValue}
                          autoFocus
                          fullwidth={true}
                          onChange={(e: ChangeEvent<HTMLInputElement>) => onSearchChange?.(e.target.value)}
                          onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
                            if (e.key === 'Enter') {
                              onSearch?.((e.target as HTMLInputElement).value)
                            }
                          }}
                        />
                      )}
                    </div>
                  </div>
                ) : (
                  <PktTextinput
                    id="header-service-search"
                    className="pkt-header-service__search-input"
                    type="search"
                    label="Søk"
                    useWrapper={false}
                    placeholder={searchPlaceholder}
                    value={searchValue}
                    onChange={(e: ChangeEvent<HTMLInputElement>) => onSearchChange?.(e.target.value)}
                    onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
                      if (e.key === 'Enter') {
                        onSearch?.((e.target as HTMLInputElement).value)
                      }
                    }}
                  />
                )}
              </>
            )}

            {isMobile && logOut && (logOutButtonPlacement === 'header' || logOutButtonPlacement === 'both') && (
              <PktButton
                skin="secondary"
                size={isMobile ? 'small' : 'medium'}
                variant="icon-only"
                iconName="exit"
                onClick={logOut}
              >
                Logg ut
              </PktButton>
            )}
          </div>
        ) : null}

        {(user ||
          (!isMobile && logOut && (logOutButtonPlacement === 'header' || logOutButtonPlacement === 'both'))) && (
          <div className="pkt-header-service__user">
            {user && (
              <div
                className="pkt-header-service__user-container"
                onBlur={(e) => handleFocusOut(e, 'user')}
                ref={userContainerRef}
              >
                <PktButton
                  className={classNames(
                    'pkt-header-service__user-button',
                    isMobile && 'pkt-header-service__user-button--mobile',
                  )}
                  skin="secondary"
                  size={isMobile ? 'small' : 'medium'}
                  state={openMenu === 'user' ? 'active' : 'normal'}
                  variant="icons-right-and-left"
                  iconName="user"
                  secondIconName={openMenu === 'user' ? 'chevron-thin-up' : 'chevron-thin-down'}
                  onClick={() => handleMenuToggle('user')}
                >
                  <span className="pkt-sr-only">Brukermeny: </span>
                  {representing?.name || user.name}
                </PktButton>
                {openMenu === 'user' && user && (
                  <div
                    className={classNames(
                      isMobile ? 'pkt-header-service__mobile-menu' : 'pkt-header-service__user-menu',
                      'is-open',
                    )}
                  >
                    <PktHeaderUserMenu
                      user={user}
                      formattedLastLoggedIn={formattedLastLoggedIn}
                      representing={representing}
                      userMenu={userMenu}
                      canChangeRepresentation={canChangeRepresentation}
                      changeRepresentation={changeRepresentation}
                      logoutOnClick={
                        logOutButtonPlacement === 'userMenu' || logOutButtonPlacement === 'both' ? logOut : undefined
                      }
                    />
                  </div>
                )}
              </div>
            )}
            {!isMobile && logOut && (logOutButtonPlacement === 'header' || logOutButtonPlacement === 'both') && (
              <PktButton skin="tertiary" size="medium" variant="icon-right" iconName="exit" onClick={logOut}>
                Logg ut
              </PktButton>
            )}
          </div>
        )}
      </header>
    )

    if (isFixed) {
      return (
        <div className="pkt-header-service-wrapper">
          {headerElement}
          <div
            className={classNames(
              'pkt-header-service-spacer',
              compact && 'pkt-header-service-spacer--compact',
              user && 'pkt-header-service-spacer--has-user',
              isMobile && 'pkt-header-service-spacer--mobile',
              isTablet && 'pkt-header-service-spacer--tablet',
            )}
          />
        </div>
      )
    }

    return headerElement
  },
)
PktHeaderService.displayName = 'PktHeaderService'
