import clsx from 'clsx'
import {ReadonlyURLSearchParams, usePathname} from 'next/navigation'
import {useRouter} from 'next/router'
import {ReactNode, useEffect, useRef} from 'react'
import {Row} from 'web/components/layout/row'
import {Carousel} from 'web/components/widgets/carousel'
import {Tooltip} from 'web/components/widgets/tooltip'
import {useDefinedSearchParams} from 'web/hooks/use-defined-search-params'
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
import {usePersistentLocalState} from 'web/hooks/use-persistent-local-state'
import {track} from 'web/lib/service/analytics'

import {Col} from './col'

export type Tab = {
  title: string
  content: ReactNode
  stackedTabIcon?: ReactNode
  inlineTabIcon?: ReactNode
  tooltip?: string
  prerender?: boolean
  className?: string
  queryString?: string
}

type TabProps = {
  tabs: Tab[]
  labelClassName?: string
  onClick?: (tabTitleOrQueryTitle: string, index: number) => void
  className?: string
  labelsParentClassName?: string
  trackingName?: string
  // Default is to lazy render tabs as they are selected. If true, it will render all tabs at once.
  renderAllTabs?: boolean
  name?: string // a unique identifier for the tabs, used for caching
}

export function MinimalistTabs(props: TabProps & {activeIndex: number}) {
  const {tabs, activeIndex, labelClassName, onClick, className, renderAllTabs, trackingName} = props

  const hasRenderedIndexRef = useRef(new Set<number>())
  hasRenderedIndexRef.current.add(activeIndex)

  return (
    <>
      <Carousel className={clsx('border-ink-200 border-b pb-1', className)} aria-label="Tabs">
        {tabs.map((tab, i) => (
          <a
            href="#"
            key={tab.queryString ?? tab.title}
            onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
              e.preventDefault()
              onClick?.(tab.queryString?.toLowerCase() ?? tab.title.toLowerCase(), i)
              if (trackingName) {
                track(trackingName, {
                  tab: tab.title,
                })
              }
            }}
            aria-current={activeIndex === i ? 'page' : undefined}
            className={clsx(
              activeIndex === i ? 'text-primary-600' : 'text-ink-400 hover:text-ink-700',
              'cursor-pointer whitespace-nowrap text-lg ',
              labelClassName,
            )}
          >
            <Tooltip text={tab.tooltip}>
              <Row className={'items-center'}>{tab.title}</Row>
            </Tooltip>
          </a>
        ))}
      </Carousel>
      {tabs
        .map((tab, i) => ({tab, i}))
        .filter(({tab, i}) => renderAllTabs || tab.prerender || hasRenderedIndexRef.current.has(i))
        .map(({tab, i}) => (
          <div key={i} className={clsx(i === activeIndex ? 'contents' : 'hidden', tab.className)}>
            {tab.content}
          </div>
        ))}
    </>
  )
}

export function ControlledTabs(props: TabProps & {activeIndex: number}) {
  const {
    tabs,
    activeIndex,
    labelClassName,
    onClick,
    className,
    renderAllTabs,
    labelsParentClassName,
    trackingName,
  } = props

  const hasRenderedIndexRef = useRef(new Set<number>())
  hasRenderedIndexRef.current.add(activeIndex)

  return (
    <>
      <Carousel
        className={clsx('border-ink-200 border-b', className)}
        labelsParentClassName={labelsParentClassName}
        aria-label="Tabs"
      >
        {tabs.map((tab, i) => (
          <a
            href="#"
            key={tab.queryString ?? tab.title}
            onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
              e.preventDefault()

              onClick?.(tab.queryString?.toLowerCase() ?? tab.title.toLowerCase(), i)

              if (trackingName) {
                track(trackingName, {
                  tab: tab.title,
                })
              }
            }}
            className={clsx(
              activeIndex === i
                ? 'border-primary-500 text-primary-600'
                : 'text-ink-500 hover:border-ink-300 hover:text-ink-700 border-transparent',
              'mr-4 inline-flex cursor-pointer flex-row gap-1 whitespace-nowrap border-b-2 px-1 py-3 text-sm font-medium ',
              labelClassName,
              'flex-shrink-0',
            )}
            aria-current={activeIndex === i ? 'page' : undefined}
          >
            <Col
              className={
                clsx()
                // tab.stackedTabIcon && (activeIndex !== i) && 'opacity-85'
              }
            >
              <Tooltip text={tab.tooltip}>
                {tab.stackedTabIcon && <Row className="justify-center">{tab.stackedTabIcon}</Row>}
                <Row className={'items-center'}>
                  <Col>
                    {tab.title.split('\n').map((line, i) => (
                      <Row className={'items-center justify-center'} key={i}>
                        {line}
                      </Row>
                    ))}
                  </Col>
                  {tab.inlineTabIcon}
                </Row>
              </Tooltip>
            </Col>
          </a>
        ))}
      </Carousel>
      {tabs
        .map((tab, i) => ({tab, i}))
        .filter(({tab, i}) => renderAllTabs || tab.prerender || hasRenderedIndexRef.current.has(i))
        .map(({tab, i}) => (
          <div key={i} className={clsx(i === activeIndex ? 'contents' : 'hidden', tab.className)}>
            {tab.content}
          </div>
        ))}
    </>
  )
}

export function UncontrolledTabs(props: TabProps & {defaultIndex?: number}) {
  const {defaultIndex, onClick, ...rest} = props
  const [activeIndex, setActiveIndex] = usePersistentInMemoryState(
    defaultIndex ?? 0,
    `tab-${props.trackingName}-${props.name ?? props.tabs[0]?.title}`,
  )
  if ((defaultIndex ?? 0) > props.tabs.length - 1) {
    console.error('default index greater than tabs length')
  }

  useEffect(() => {
    if (typeof window !== 'undefined' && /^#\d+$/.test(window.location.hash)) {
      const i = Number(window.location.hash.slice(1))
      if (i < props.tabs.length) setActiveIndex(i)
    }
  }, [])

  return (
    <ControlledTabs
      {...rest}
      activeIndex={activeIndex}
      onClick={(titleOrQueryTitle, i) => {
        setActiveIndex(i)
        onClick?.(titleOrQueryTitle, i)
      }}
      labelsParentClassName={'gap-0 xs:gap-4'}
    />
  )
}

const isTabSelected = (params: ReadonlyURLSearchParams, queryParam: string, tab: Tab) => {
  const selected = params.get(queryParam)
  if (typeof selected === 'string') {
    return (tab.queryString?.toLowerCase() ?? tab.title.toLowerCase()) === selected
  } else {
    return false
  }
}

export function QueryUncontrolledTabs(
  props: TabProps & {
    defaultIndex?: number
    scrollToTop?: boolean
    minimalist?: boolean
    saveTabInLocalStorageKey?: string
  },
) {
  const {tabs, minimalist, onClick, scrollToTop, saveTabInLocalStorageKey, ...rest} = props
  const router = useRouter()
  const pathName = usePathname()
  const {searchParams, createQueryString} = useDefinedSearchParams()
  const selectedIdx = tabs.findIndex((t) => isTabSelected(searchParams, 'tab', t))
  const [savedTabIndex, setSavedTabIndex] = usePersistentLocalState<number | undefined>(
    undefined,
    saveTabInLocalStorageKey ?? '',
  )
  const defaultIndex =
    (saveTabInLocalStorageKey ? savedTabIndex : undefined) ?? props.defaultIndex ?? 0
  const activeIndex = selectedIdx !== -1 ? selectedIdx : defaultIndex

  useEffect(() => {
    if (onClick) {
      onClick(tabs[activeIndex].queryString ?? tabs[activeIndex].title, activeIndex)
    }
    if (saveTabInLocalStorageKey) setSavedTabIndex(activeIndex)
  }, [activeIndex])
  if (minimalist) {
    return (
      <MinimalistTabs
        {...rest}
        tabs={tabs}
        activeIndex={activeIndex}
        onClick={(title) => {
          if (scrollToTop) window.scrollTo({top: 0})
          router.replace(pathName + '?' + createQueryString('tab', title), undefined, {
            shallow: true,
          })
        }}
      />
    )
  }
  return (
    <ControlledTabs
      {...rest}
      tabs={tabs}
      activeIndex={activeIndex}
      onClick={(title) => {
        if (scrollToTop) window.scrollTo({top: 0})
        router.replace(pathName + '?' + createQueryString('tab', title), undefined, {shallow: true})
      }}
    />
  )
}
