import React, { useEffect, useReducer } from 'react'
import { useRouter } from 'next/router'
import { clearBookingData, loadBookingData } from '../helpers/storage'

import { createBooking } from '../helpers/booking'
import { Booking } from '../db/booking'

export type BookFormData = Omit<Booking, 'uuid'> & {
  storeData?: boolean
}

type BookingProviderState = {
  postData?: boolean
  postDataError?: string
  postDataSuccess?: boolean
  formData: BookFormData
  formDataChanged: string[]
  booking?: Booking
  dataStored: boolean
  dataStoredLoaded: boolean
}

type BookProvider = {
  state: BookingProviderState
  dispatch: React.Dispatch<BookAction>
  onChange: (data: object) => void
  onChangeEvent: (event: React.ChangeEvent<React.ElementRef<'input'>>) => void
  onSubmit: () => void
  forgetData: () => void
}

type BookAction = {
  type: string
  payload?: any
}

export const BookContext: React.Context<BookProvider> = React.createContext<
  BookProvider
>(null)

export const ACTIONS = {
  SET_FORM_DATA: 'setFormData',
  POST_DATA: 'postData',
  POST_DATA_ERROR: 'postDataError',
  DATA_STORED: 'dataStored',
  DATA_STORED_LOADED: 'dataStoredLoaded',
  DATA_STORED_FORGOTTEN: 'dataStoredForgotten',
}

function reducer(state: BookingProviderState, action: BookAction) {
  switch (action.type) {
    case ACTIONS.SET_FORM_DATA:
      return {
        ...state,
        formData: {
          ...state.formData,
          ...action.payload,
        },
        formDataChanged: [
          ...state.formDataChanged,
          ...Object.keys(action.payload),
        ],
      }
    case ACTIONS.POST_DATA:
      return {
        ...state,
        postData: true,
        postDataError: null,
        postDataSuccess: null,
      }
    case ACTIONS.POST_DATA_ERROR:
      return {
        ...state,
        postData: false,
        postDataError: action.payload,
        postDataSuccess: null,
      }
    case ACTIONS.DATA_STORED_LOADED:
      return {
        ...state,
        dataStoredLoaded: true,
        formData: {
          ...state.formData,
          ...action.payload,
        },
      }
    case ACTIONS.DATA_STORED_FORGOTTEN:
      return {
        ...state,
        dataStored: undefined,
        dataStoredLoaded: undefined,
        formData: { ...initialState.formData },
      }
    case ACTIONS.DATA_STORED:
      return {
        ...state,
        dataStored: action.payload,
      }
    default:
      throw new Error(`Unkown Action type ${action.type}`)
  }
}

const initialState: BookingProviderState = {
  postData: false,
  postDataError: null,
  postDataSuccess: null,
  formData: {
    startDate: '',
    endDate: '',
    purpose: '',
    org: '',
    destination: '',
    name: '',
    email: '',
    phone: '',
    street: '',
    zip: '',
    city: '',
  },
  formDataChanged: [],
  dataStored: undefined,
  dataStoredLoaded: undefined,
}

export default function BookProvider({ children }) {
  const router = useRouter()
  const [state, dispatch] = useReducer(reducer, initialState)

  useEffect(() => {
    const data = loadBookingData()
    if (data !== null) {
      dispatch({ type: ACTIONS.DATA_STORED_LOADED, payload: data })
    }
  }, [])

  const onChangeEvent = (
    event: React.ChangeEvent<React.ElementRef<'input'>>
  ) => {
    const { name, value } = event.target

    dispatch({
      type: ACTIONS.SET_FORM_DATA,
      payload: { [name]: value },
    })
  }

  const onChange = (data: object) => {
    dispatch({
      type: ACTIONS.SET_FORM_DATA,
      payload: data,
    })
  }

  const onSubmit = async () => {
    dispatch({ type: ACTIONS.POST_DATA })

    try {
      const booking = await createBooking(state.formData)
      router.push(`/booking/${booking.uuid}/stored`)
    } catch (error) {
      console.error(error)
      dispatch({ type: ACTIONS.POST_DATA_ERROR, payload: error.message })
    }
  }

  const forgetData = () => {
    clearBookingData()
    dispatch({ type: ACTIONS.DATA_STORED_FORGOTTEN })
  }

  return (
    <BookContext.Provider
      value={{
        state,
        dispatch,
        onChangeEvent,
        onChange,
        onSubmit,
        forgetData,
      }}
    >
      {children}
    </BookContext.Provider>
  )
}
