import {
  createContext,
  useContext,
  useState,
  useRef,
  useEffect,
  useCallback,
  MutableRefObject,
  Dispatch,
  SetStateAction,
  ReactNode,
} from 'react'
import styled, { keyframes } from 'styled-components'
import { useDebouncedCallback } from 'use-debounce'
import Header from './header.js'
import { InteractiveIcon } from './icon.js'
import { Theme } from './theme.js'
import { noop } from '@navinc/utils'
import isRebrand from './is-rebrand.js'

const moveOffScreen = keyframes`
  from { right: 0%; }
  to   { right: -100%; }
`
const moveOnScreen = keyframes`
  from { right: -100%; }
  to   { right: 0%; }
`

const suddenIn = keyframes`
  from { width: 100%; opacity: 0; }
  to   { width: 100%; opacity: 1; }
`
const suddenOut = keyframes`
  0%    { width: 100%; opacity: 1; }
  99.9% { width: 100%; }
  100%  { width: 0%; opacity: 0; }
`

const Backdrop = styled.div<{ isOpen?: boolean }>`
  align-items: flex-start;
  animation: 0.2s ${({ theme }) => theme.materialTransitionTiming} both ${({ isOpen }) =>
  isOpen ? suddenIn : suddenOut} ;
  background-color: hsla(0, 0%, 0%, .2)};
  box-sizing: border-box;
  display: flex;
  height: 100%;
  justify-content: flex-end;
  overflow: hidden;
  pointer-events: ${({ isOpen }) => (isOpen ? 'initial' : 'none')};
  position: fixed;
  right: 0;

  & * {
    box-sizing: border-box;
  }
`

const drawerHeightVariations = (theme: Theme, type?: HeightVariation, mobileFullPage?: boolean) => {
  switch (type) {
    case 'dynamic':
      return `
    border-bottom-left-radius: 4px;
    border-top-left-radius: 4px;
    height: ${mobileFullPage ? '100%' : 'auto'} ;
    margin-top: ${mobileFullPage ? '0' : '100px'};
    max-height: ${!mobileFullPage && 'calc(100vh - 100px)'};
    min-height: 80px;
    @media (${theme.forLargerThanPhone}) {
    height: auto;
    max-height: calc(100vh - 100px);
    }
  `
    case 'full':
      return `
    align-content: flex-start;
    height: 100vh;
  `
    default:
      return null
  }
}

const contentHeightVariations = {
  dynamic: 'max-height: calc(100vh - 160px);',
  full: 'max-height: calc(100vh - 65px);',
} as const

type HeightVariation = keyof typeof contentHeightVariations

const CloseButton = styled(InteractiveIcon).attrs(() => ({ name: 'actions/close' }))`
  cursor: pointer;
`

const Content = styled.div<{ heightVariation: HeightVariation }>`
  overflow: auto;
  grid-column: 1 / -1;
  font-family: ${({ theme }) => theme.fontPrimary}
    ${({ heightVariation }) => contentHeightVariations[heightVariation] ?? contentHeightVariations.dynamic};
`

type Variation = 'blue' | 'default'

type DrawerProps = {
  isOpen?: boolean
  variation?: Variation
  heightVariation?: HeightVariation
  mobileFullPage?: boolean
  isTouching?: boolean
  orientBottom?: boolean
}
const Drawer = styled.div<DrawerProps>`
  animation: 0.2s ${({ theme }) => theme.materialTransitionTiming} both
    ${({ isOpen }) => (isOpen ? moveOnScreen : moveOffScreen)};

  background-color: ${({ variation = 'default', theme }) => {
    if (isRebrand(theme)) return theme.navSecondary100
    const colorVariation = {
      blue: theme.bubbleBlue100,
      default: theme.white,
    }
    return colorVariation[variation] ?? colorVariation.default
  }};
  display: grid;
  grid-template-columns: 1fr min-content;
  grid-template-rows: ${({ mobileFullPage }) => mobileFullPage && 'auto 1fr'};
  gap: 16px;
  border-right: solid 1px ${({ theme }) => theme.border};
  overflow: hidden;
  padding: 16px;
  // only use transition when not touching, otherwise dragging feels sluggish
  transition: ${({ isTouching, theme }) => !isTouching && `transform 0.2s ${theme.materialTransitionTiming};`}
  ${({ theme, heightVariation, mobileFullPage }) =>
    drawerHeightVariations(theme, heightVariation, mobileFullPage) ?? drawerHeightVariations(theme, 'dynamic')}

  width: 100%;

  @media (${({ theme }) => theme.forLargerThanPhone}) {
    position: ${({ orientBottom }) => orientBottom && `absolute`};
    bottom: ${({ orientBottom }) => orientBottom && `0`};
    width:  ${({ orientBottom }) => (orientBottom ? `calc(100% - 260px)` : `485px`)};
    padding: 24px 24px 32px;
    max-width: calc(100vw - 55px);

  }
`

type CloseHandler = () => void
type InfoDrawerContextValue = {
  mobileFullPage: boolean
  orientBottom: boolean
  isOpen: boolean
  setIsOpen: Dispatch<SetStateAction<boolean>>
  close: () => void
  content?: ReactNode
  title?: ReactNode
  setInfoDrawer(config: InfoDrawerConfig): void
  closeHandlersRef: MutableRefObject<CloseHandler[]>
}

const InfoDrawerContext = createContext<InfoDrawerContextValue>({
  mobileFullPage: false,
  orientBottom: false,
  isOpen: false,
  setIsOpen: noop,
  content: null,
  title: null,
  setInfoDrawer: noop,
  closeHandlersRef: { current: [] },
  close: noop,
})

type InfoDrawerConfig = {
  isOpen?: boolean
  mobileFullPage?: boolean
  orientBottom?: boolean
  title?: ReactNode
  content?: ReactNode
}

export const InfoDrawerProvider = ({ children }) => {
  const [mobileFullPage, setMobileFullPage] = useState(false)
  const [orientBottom, setOrientBottom] = useState(false)
  const [content, setContent] = useState<ReactNode>(null)
  const [title, setTitle] = useState<ReactNode>(null)
  const [isOpen, setIsOpen] = useState(false)
  const closeHandlersRef = useRef<CloseHandler[]>([])

  const close = useCallback(() => {
    setIsOpen(false)
    closeHandlersRef.current.forEach((closeHandler) => closeHandler())
  }, [])

  const setInfoDrawer = useCallback(
    (config: InfoDrawerConfig = {}) => {
      const cfg = {
        // in order to keep the value of orientBottom and mobileFullPage we need to check the config if the drawer is open to prevent flashing when orientBottom is true
        mobileFullPage: config.isOpen ? false : mobileFullPage,
        orientBottom: config.isOpen ? false : orientBottom,
        title,
        content,
        isOpen,
        ...config,
      }
      setMobileFullPage(cfg.mobileFullPage)
      setOrientBottom(cfg.orientBottom)
      if (isOpen && !config.isOpen) {
        close()
      } else {
        setIsOpen(cfg.isOpen)
      }
      setTitle(cfg.title)
      setContent(cfg.content)
    },
    [close, content, isOpen, title, orientBottom, mobileFullPage]
  )
  return (
    <InfoDrawerContext.Provider
      value={{
        mobileFullPage,
        orientBottom,
        content,
        isOpen,
        setIsOpen,
        title,
        closeHandlersRef,
        close,
        setInfoDrawer,
      }}
    >
      {children}
    </InfoDrawerContext.Provider>
  )
}
export const useInfoDrawer = ({ onClose }: { onClose?: CloseHandler } = {}) => {
  const { mobileFullPage, orientBottom, content, title, setInfoDrawer, closeHandlersRef, isOpen } =
    useContext(InfoDrawerContext)
  if (onClose && !closeHandlersRef.current.includes(onClose)) {
    closeHandlersRef.current = closeHandlersRef.current.concat(onClose)
  }

  // When the component unmounts, we want to clean up our onClose handler.
  useEffect(
    () => () => {
      closeHandlersRef.current = closeHandlersRef.current.filter((closeHandler) => closeHandler !== onClose)
    },
    [closeHandlersRef, onClose]
  )

  return {
    mobileFullPage,
    orientBottom,
    content,
    title,
    isOpen,
    setInfoDrawer,
  }
}

export const InfoDrawer = ({
  variation,
  heightVariation = 'dynamic',
  className,
}: {
  variation?: Variation
  heightVariation?: HeightVariation
  className?: string
}) => {
  const { mobileFullPage, orientBottom, content, title, isOpen, setInfoDrawer } = useInfoDrawer()
  const [moveStartX, setMoveStartX] = useState(0)
  const [translateX, setTranslateX] = useState(0)
  const onTouchMove = useDebouncedCallback(
    (e) => {
      onTouchMove.cancel()
      e.persist()
      setTranslateX(e.touches[0].clientX)
    },
    2,
    { maxWait: 5 }
  )

  return (
    <Backdrop
      isOpen={isOpen}
      className={className}
      data-testid="info-drawer:backdrop"
      onTouchStart={(e) => setMoveStartX(e.touches[0].clientX)}
      onTouchMove={onTouchMove}
      onTouchEnd={() => {
        onTouchMove.cancel()
        translateX - moveStartX > 180 && setInfoDrawer({ isOpen: false })
        setMoveStartX(0)
        setTranslateX(0)
      }}
      onClick={(e) => {
        e.persist()
        if ('dataset' in e.target && (e.target as any).dataset.testid === 'info-drawer:backdrop') {
          setInfoDrawer({ isOpen: false })
        }
      }}
    >
      <Drawer
        mobileFullPage={mobileFullPage}
        orientBottom={orientBottom}
        style={{
          transform: `translateX(${isOpen ? Math.max(translateX - moveStartX, 0) : 485}px)`,
        }}
        isOpen={isOpen}
        isTouching={moveStartX !== 0}
        variation={variation}
        heightVariation={heightVariation}
        data-testid="info-drawer:drawer"
      >
        <Header size="sm">{title}</Header>
        <CloseButton onClick={() => setInfoDrawer({ isOpen: false })} data-testid="info-drawer:close-button" />
        <Content heightVariation={heightVariation}>{content}</Content>
      </Drawer>
    </Backdrop>
  )
}
export default styled(InfoDrawer)``
