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

interface WizardFormData {
  startDate: string
  endDate: string
  purpose: string
  org: string
  destination: string
  name: string
  email: string
  street: string
  zip: string
  city: string
}

interface Booking {
  uuid: string
}

interface WizardStoreState {
  postData?: boolean
  postDataError?: string
  postDataSuccess?: boolean
  formData: WizardFormData
  formDataChanged: string[]
  booking?: Booking
  dataStored: boolean
  dataStoredLoaded: boolean
}

interface WizardStore {
  state: WizardStoreState
  dispatch: React.Dispatch<WizardAction>
  onChange: (data: object) => void
  onChangeEvent: (event: React.ChangeEvent<React.ElementRef<'input'>>) => void
  onSubmit: () => void
  storeData: (value: boolean) => void
  forgetData: () => void
}

interface WizardAction {
  type: string
  payload?: any
}

export const WizardContext: React.Context<WizardStore> = React.createContext<
  WizardStore
>(null)

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

function reducer(state: WizardStoreState, action: WizardAction) {
  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.POST_DATA_SUCCESS:
      return {
        ...state,
        formData: {
          ...state.formData,
        },
        booking: { ...action.payload },
        postData: false,
        postDataError: null,
        postDataSuccess: true,
      }
    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: WizardStoreState = {
  postData: false,
  postDataError: null,
  postDataSuccess: null,
  formData: {
    startDate: '',
    endDate: '',
    purpose: '',
    org: '',
    destination: '',
    name: '',
    email: '',
    street: '',
    zip: '',
    city: '',
  },
  formDataChanged: [],
  dataStored: undefined,
  dataStoredLoaded: undefined,
}

async function createBooking(formData: WizardFormData) {
  const response = await fetch('/api/booking', {
    method: 'POST',
    mode: 'cors',
    cache: 'no-cache',
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json',
    },
    referrerPolicy: 'no-referrer',
    body: JSON.stringify(formData),
  })
  return response.json()
}

export default function WizardStore({ children }) {
  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)
      dispatch({ type: ACTIONS.POST_DATA_SUCCESS, payload: booking })
    } catch (error) {
      console.error(error)
      dispatch({ type: ACTIONS.POST_DATA_ERROR, payload: error.message })
    }
  }

  const storeData = (value: boolean) => {
    if (value) {
      storeBookingData(state.booking)
    }

    dispatch({ type: ACTIONS.DATA_STORED, payload: value })
  }

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

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