import { FormModel, AbstractFormField } from '../interfaces'
import {
    SET_FORM_FIELD,
    DESTROY_FORM,
    RESET_FORM,
    SUBMIT_FORM_VALID,
    SUBMIT_FORM_INVALID,
    SEARCH_ADDRESS,
} from '../constants'
import {
    FormAction,
    ResetFormAction,
    DestroyFormAction,
    SetFormFieldAction,
    SearchAddressAction,
    SearchAddressSuccessAction,
    SearchAddressErrorAction,
} from '../actions'

export interface FormState {
    [formName: string]: AbstractFormField<any> | FormModel
}

export const formReducer = (state: FormState = {}, action: FormAction): FormState => {
    switch (action.type) {
        case SET_FORM_FIELD:
            return setFormField(state, action)
        case DESTROY_FORM:
            return destroyForm(state, action)
        case RESET_FORM:
            return resetForm(state, action)
        case SUBMIT_FORM_VALID:
        case SUBMIT_FORM_INVALID:
            return submitForm(state, action)
        default:
            return state
    }
}

const setFormField = (state: FormState, action: SetFormFieldAction): FormState => {
    if (action.formName) {
        return {
            ...state,
            [action.formName]: {
                ...state[action.formName],
                [action.name]: action.field,
            },
        }
    } else {
        return {
            ...state,
            [action.name]: action.field,
        }
    }
}

const destroyForm = (state: FormState, action: DestroyFormAction): FormState => {
    const newState = { ...state }
    delete newState[action.formName]
    return newState
}

/**
 * Clear the form data for a named form
 *
 * @param state
 * @param action
 */
const resetForm = (state: FormState, action: ResetFormAction): FormState => {
    const s = { ...state, [action.formName]: { ...state[action.formName]}}
    Object.keys(s[action.formName]).forEach(field => {
        (s[action.formName] as any)[field] = {
            ...(s[action.formName] as any)[field],
            value: '',
        }
    });
    return s
}

/**
 * When a form is submitted we can immediately
 * make every field in the form as being submitted
 *
 * @param state
 * @param action
 */
const submitForm = (state: FormState, action: FormAction): FormState => {
    const form = state[action.formName]

    if (!form) {
        throw new Error(`A form with the name ${action.formName} does not exist`)
    }
    const newState: FormState = {
        ...state,
        [action.formName]: {
            ...form,
        },
    }
    for (const key of Object.keys(form)) {
        (newState[action.formName] as FormModel)[key] = {
            ...(newState[action.formName] as FormModel)[key],
            submitted: true,
        }
    }
    return newState
}

// TODO - see if these can be generalized for all fields
const setAddressLoading = (state: FormState, action: SearchAddressAction) => ({
    ...state,
    [action.args.formName]: {
        ...state[action.args.formName],
        [action.args.name]: {
            ...(state[action.args.formName] as any)[action.args.name],
            loading: true,
        },
    },
})

const setAddressOptions = (state: FormState, action: SearchAddressSuccessAction) => ({
    ...state,
    [action.args.formName]: {
        ...state[action.args.formName],
        [action.args.name]: {
            ...(state[action.args.formName] as any)[action.args.name],
            loading: true,
        },
    },
})

const setAddressErrors = (state: FormState, action: SearchAddressErrorAction) => ({
    ...state,
    [action.args.formName]: {
        ...state[action.args.formName],
        [action.args.name]: {
            ...(state[action.args.formName] as any)[action.args.name],
            loading: true,
        },
    },
})