text: import { createAction, handleActions } from 'redux-actions';
  import { reducer as reduxFormReducer } from 'redux-form';
  
  import { createTypes } from '../../../../utils/stateManagement';
  import { createAsyncTypes } from '../../../utils/createTypes';
  
  import { createPoly } from '../utils/polyvinyl';
  import challengeModalEpic from './challenge-modal-epic';
  import completionEpic from './completion-epic';
  import codeLockEpic from './code-lock-epic';
  import createQuestionEpic from './create-question-epic';
  import codeStorageEpic from './code-storage-epic';
  
  import { createIdToNameMapSaga } from './id-to-name-map-saga';
  import { createExecuteChallengeSaga } from './execute-challenge-saga';
  import { createCurrentChallengeSaga } from './current-challenge-saga';
  import { challengeTypes } from '../../../../utils/challengeTypes';
  
  export const ns = 'challenge';
  export const backendNS = 'backendChallenge';
  
  const initialState = {
    challengeFiles: {},
    challengeIdToNameMap: {},
    challengeMeta: {
      id: '',
      nextChallengePath: '/',
      introPath: '',
      challengeType: -1
    },
    challengeTests: [],
    consoleOut: '',
    isCodeLocked: false,
    isBuildEnabled: true,
    modal: {
      completion: false,
      help: false,
      video: false,
      reset: false
    },
    projectFormValues: {},
    successMessage: 'Happy Coding!'
  };
  
  export const types = createTypes(
    [
      'createFiles',
      'createQuestion',
      'initTests',
      'initConsole',
      'initLogs',
      'updateConsole',
      'updateChallengeMeta',
      'updateFile',
      'updateJSEnabled',
      'updateProjectFormValues',
      'updateSuccessMessage',
      'updateTests',
      'updateLogs',
  
      'logsToConsole',
  
      'lockCode',
      'unlockCode',
      'disableBuildOnError',
      'storedCodeFound',
      'noStoredCodeFound',
  
      'closeModal',
      'openModal',
  
      'previewMounted',
      'challengeMounted',
      'checkChallenge',
      'executeChallenge',
      'resetChallenge',
      'submitChallenge',
  
      'moveToTab',
  
      ...createAsyncTypes('fetchIdToNameMap')
    ],
    ns
  );
  
  export const epics = [
    challengeModalEpic,
    codeLockEpic,
    completionEpic,
    createQuestionEpic,
    codeStorageEpic
  ];
  
  export const sagas = [
    ...createIdToNameMapSaga(types),
    ...createExecuteChallengeSaga(types),
    ...createCurrentChallengeSaga(types)
  ];
  
  export const createFiles = createAction(types.createFiles, challengeFiles =>
    Object.keys(challengeFiles)
      .filter(key => challengeFiles[key])
      .map(key => challengeFiles[key])
      .reduce(
        (challengeFiles, file) => ({
          ...challengeFiles,
          [file.key]: {
            ...createPoly(file),
            seed: file.contents.slice(0)
          }
        }),
        {}
      )
  );
  
  export const fetchIdToNameMap = createAction(types.fetchIdToNameMap);
  export const fetchIdToNameMapComplete = createAction(
    types.fetchIdToNameMapComplete
  );
  export const fetchIdToNameMapError = createAction(types.fetchIdToNameMapError);
  
  export const createQuestion = createAction(types.createQuestion);
  export const initTests = createAction(types.initTests);
  export const updateTests = createAction(types.updateTests);
  
  export const initConsole = createAction(types.initConsole);
  export const initLogs = createAction(types.initLogs);
  export const updateChallengeMeta = createAction(types.updateChallengeMeta);
  export const updateFile = createAction(types.updateFile);
  export const updateConsole = createAction(types.updateConsole);
  export const updateLogs = createAction(types.updateLogs);
  export const updateJSEnabled = createAction(types.updateJSEnabled);
  export const updateProjectFormValues = createAction(
    types.updateProjectFormValues
  );
  export const updateSuccessMessage = createAction(types.updateSuccessMessage);
  
  export const logsToConsole = createAction(types.logsToConsole);
  
  export const lockCode = createAction(types.lockCode);
  export const unlockCode = createAction(types.unlockCode);
  export const disableBuildOnError = createAction(types.disableBuildOnError);
  export const storedCodeFound = createAction(types.storedCodeFound);
  export const noStoredCodeFound = createAction(types.noStoredCodeFound);
  
  export const closeModal = createAction(types.closeModal);
  export const openModal = createAction(types.openModal);
  
  export const previewMounted = createAction(types.previewMounted);
  export const challengeMounted = createAction(types.challengeMounted);
  export const checkChallenge = createAction(types.checkChallenge);
  export const executeChallenge = createAction(types.executeChallenge);
  export const resetChallenge = createAction(types.resetChallenge);
  export const submitChallenge = createAction(types.submitChallenge);
  
  export const moveToTab = createAction(types.moveToTab);
  
  export const currentTabSelector = state => state[ns].currentTab;
  export const challengeFilesSelector = state => state[ns].challengeFiles;
  export const challengeIdToNameMapSelector = state =>
    state[ns].challengeIdToNameMap;
  export const challengeMetaSelector = state => state[ns].challengeMeta;
  export const challengeTestsSelector = state => state[ns].challengeTests;
  export const consoleOutputSelector = state => state[ns].consoleOut;
  export const isCodeLockedSelector = state => state[ns].isCodeLocked;
  export const isCompletionModalOpenSelector = state =>
    state[ns].modal.completion;
  export const isHelpModalOpenSelector = state => state[ns].modal.help;
  export const isVideoModalOpenSelector = state => state[ns].modal.video;
  export const isResetModalOpenSelector = state => state[ns].modal.reset;
  export const isBuildEnabledSelector = state => state[ns].isBuildEnabled;
  export const successMessageSelector = state => state[ns].successMessage;
  
  export const backendFormValuesSelector = state => state.form[backendNS] || {};
  export const projectFormValuesSelector = state =>
    state[ns].projectFormValues || {};
  
  export const challengeDataSelector = state => {
    const { challengeType } = challengeMetaSelector(state);
    let challengeData = { challengeType };
    if (
      challengeType === challengeTypes.js ||
      challengeType === challengeTypes.bonfire
    ) {
      challengeData = {
        ...challengeData,
        files: challengeFilesSelector(state)
      };
    } else if (challengeType === challengeTypes.backend) {
      const { solution: { value: url } = {} } = backendFormValuesSelector(state);
      challengeData = {
        ...challengeData,
        url
      };
    } else if (
      challengeType === challengeTypes.frontEndProject ||
      challengeType === challengeTypes.backendEndProject
    ) {
      challengeData = {
        ...challengeData,
        ...projectFormValuesSelector(state)
      };
    } else if (
      challengeType === challengeTypes.html ||
      challengeType === challengeTypes.modern
    ) {
      const { required = [], template = '' } = challengeMetaSelector(state);
      challengeData = {
        ...challengeData,
        files: challengeFilesSelector(state),
        required,
        template
      };
    }
    return challengeData;
  };
  
  export const reducer = handleActions(
    {
      [types.fetchIdToNameMapComplete]: (state, { payload }) => ({
        ...state,
        challengeIdToNameMap: payload
      }),
      [types.createFiles]: (state, { payload }) => ({
        ...state,
        challengeFiles: payload
      }),
      [types.updateFile]: (state, { payload: { key, editorValue } }) => ({
        ...state,
        challengeFiles: {
          ...state.challengeFiles,
          [key]: {
            ...state.challengeFiles[key],
            contents: editorValue
          }
        }
      }),
      [types.storedCodeFound]: (state, { payload }) => ({
        ...state,
        challengeFiles: payload
      }),
  
      [types.initTests]: (state, { payload }) => ({
        ...state,
        challengeTests: payload
      }),
      [types.updateTests]: (state, { payload }) => ({
        ...state,
        challengeTests: payload
      }),
  
      [types.initConsole]: (state, { payload }) => ({
        ...state,
        consoleOut: payload
      }),
      [types.updateConsole]: (state, { payload }) => ({
        ...state,
        consoleOut: state.consoleOut + '\n' + payload
      }),
      [types.initLogs]: state => ({
        ...state,
        logsOut: []
      }),
      [types.updateLogs]: (state, { payload }) => ({
        ...state,
        logsOut: [...state.logsOut, payload]
      }),
      [types.logsToConsole]: (state, { payload }) => ({
        ...state,
        consoleOut:
          state.consoleOut +
          (state.logsOut.length
            ? '\n' + payload + '\n' + state.logsOut.join('\n')
            : '')
      }),
      [types.updateChallengeMeta]: (state, { payload }) => ({
        ...state,
        challengeMeta: { ...payload }
      }),
  
      [types.resetChallenge]: state => ({
        ...state,
        currentTab: 2,
        challengeFiles: {
          ...Object.keys(state.challengeFiles)
            .map(key => state.challengeFiles[key])
            .reduce(
              (files, file) => ({
                ...files,
                [file.key]: {
                  ...file,
                  contents: file.seed.slice()
                }
              }),
              {}
            )
        },
        challengeTests: state.challengeTests.map(({ text, testString }) => ({
          text,
          testString
        })),
        consoleOut: ''
      }),
      [types.updateProjectFormValues]: (state, { payload }) => ({
        ...state,
        projectFormValues: payload
      }),
  
      [types.lockCode]: state => ({
        ...state,
        isCodeLocked: true
      }),
      [types.unlockCode]: state => ({
        ...state,
        isBuildEnabled: true,
        isCodeLocked: false
      }),
      [types.disableBuildOnError]: (state, { payload }) => ({
        ...state,
        consoleOut: state.consoleOut + ' \n' + payload,
        isBuildEnabled: false
      }),
  
      [types.updateSuccessMessage]: (state, { payload }) => ({
        ...state,
        successMessage: payload
      }),
      [types.closeModal]: (state, { payload }) => ({
        ...state,
        modal: {
          ...state.modal,
          [payload]: false
        }
      }),
      [types.openModal]: (state, { payload }) => ({
        ...state,
        modal: {
          ...state.modal,
          [payload]: true
        }
      }),
      [types.moveToTab]: (state, { payload }) => ({
        ...state,
        currentTab: payload
      }),
      [types.executeChallenge]: state => ({
        ...state,
        currentTab: 3
      })
    },
    initialState
  );
  
  const resetProjectFormValues = handleActions(
    {
      [types.updateProjectFormValues]: (state, { payload: { solution } }) => {
        if (!solution) {
          return {
            ...state,
            solution: {},
            githubLink: {}
          };
        }
        return state;
      }
    },
    {}
  );
  
  export const formReducer = reduxFormReducer.plugin({
    'frond-end-form': resetProjectFormValues,
    'back-end-form': resetProjectFormValues
  });
  