import { createContext, useCallback, useContext, useReducer } from 'react'
import BannerToast from './banner-toast.js'
import styled from 'styled-components'
import { uuidBrowser } from '@navinc/utils'

const CLEAR_MESSAGE = 'CLEAR_MESSAGE'
const SHOW_MESSAGE = 'SHOW_MESSAGE'

interface Message {
  id?: string
  isDismissible?: boolean
}

type State = {
  messages: Message[]
}

type Action = { type: typeof CLEAR_MESSAGE; id: string } | { type: typeof SHOW_MESSAGE; props: Message }

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case CLEAR_MESSAGE: {
      return {
        ...state,
        messages: state.messages.filter((message) => message.id !== action.id),
      }
    }

    case SHOW_MESSAGE: {
      // if we already have a message with the give id, we want to replace the old one.
      const hasMessageOfId = state.messages.some(({ id }) => id === action.props.id)
      return {
        ...state,
        messages: hasMessageOfId
          ? state.messages.map((message) => {
              return message.id === action.props.id ? action.props : message
            })
          : [
              ...state.messages,
              {
                ...action.props,
              },
            ],
      }
    }

    default:
      return state
  }
}

const toastContext = createContext({
  showMessage: (_msg: Message) => {},
  clearMessage: (_id?: string) => {},
  getMessages: (): Message[] => [],
})

export const ToastProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, { messages: [] })

  const clearMessage = useCallback((id) => dispatch({ type: CLEAR_MESSAGE, id }), [dispatch])
  const showMessage = useCallback(
    ({ id = uuidBrowser(), ...props }) => {
      dispatch({ type: SHOW_MESSAGE, props: { id, ...props } })
    },
    [dispatch]
  )
  const getMessages = useCallback(() => state.messages, [state])

  return <toastContext.Provider value={{ showMessage, clearMessage, getMessages }}>{children}</toastContext.Provider>
}

const MessageContainer = styled.div`
  display: flex;
  justify-content: center;
  position: relative;
  width: 100%;
`

const Grid = styled.div`
  display: grid;
  grid-gap: 8px;
  max-width: 544px;
  width: 100%;
  position: absolute;
  top: 8px;
`

export const useToast = () => useContext(toastContext)

export const Toast = ({ className }) => {
  const { clearMessage, getMessages } = useToast()
  const messages = getMessages()

  return (
    <MessageContainer className={className}>
      <Grid>
        {messages.map(({ id, isDismissible, ...message } = {}) => (
          <BannerToast
            data-testid={`global-message-${id}`}
            key={id}
            shouldNotTimeout={isDismissible}
            onDismiss={() => clearMessage(id)}
            {...message}
          />
        ))}
      </Grid>
    </MessageContainer>
  )
}
