import Vue from 'vue'
import hljs from 'highlight.js'
import * as api from '@/api'
import {nameSpacer} from '@/util/helpers'

export const subNotesNamespace = nameSpacer('submissionNotes/')

export const subNotesMut = Object.freeze({
  SET_SUBMISSION: 'SET_SUBMISSION',
  SET_ORIG_FEEDBACK: 'SET_ORIG_FEEDBACK',
  SET_SHOW_FEEDBACK: 'SET_SHOW_FEEDBACK',
  UPDATE_FEEDBACK_LINE: 'UPDATE_FEEDBACK_LINE',
  UPDATE_FEEDBACK_SCORE: 'UPDATE_FEEDBACK_SCORE',
  DELETE_FEEDBACK_LINE: 'DELETE_FEEDBACK_LINE',
  TOGGLE_EDITOR_ON_LINE: 'TOGGLE_EDITOR_ON_LINE',
  MARK_COMMENT_FOR_DELETION: 'MARK_COMMENT_FOR_DELETION',
  UN_MARK_COMMENT_FOR_DELETION: 'UN_MARK_COMMENT_FOR_DELETION',
  RESET_UPDATED_FEEDBACK: 'RESET_UPDATED_FEEDBACK',
  RESET_STATE: 'RESET_STATE'
})

function initialState () {
  return {
    assignment: '',
    submission: {
      text: '',
      pk: ''
    },
    ui: {
      showEditorOnLine: {},
      selectedCommentOnLine: {},
      showFeedback: true
    },
    hasOrigFeedback: false,
    origFeedback: {
      score: null,
      isFinal: false,
      feedbackLines: {}
    },
    updatedFeedback: {
      score: null,
      feedbackLines: {}
    },
    commentsMarkedForDeletion: {}
  }
}

const submissionNotes = {
  namespaced: true,
  state: initialState(),
  getters: {
    submissionType: (state, getters, rootState) => {
      return rootState.submissionTypes[state.submission.type]
    },
    // highlight the submission the reduce the string
    // submission.text into an object where the keys are the
    // line indexes starting at one and the values the corresponding submission line
    // this makes iterating over the submission much more pleasant
    submission: (state, getters) => {
      const language = getters.submissionType
        ? getters.submissionType.programmingLanguage
        : 'c'
      const highlighted = hljs.highlight(language, state.submission.text, true).value
      return highlighted.split('\n').reduce((acc, cur, index) => {
        acc[index + 1] = cur
        return acc
      }, {})
    },
    score: state => {
      return state.updatedFeedback.score !== null ? state.updatedFeedback.score : state.origFeedback.score
    },
    workInProgress: state => {
      const openEditor = Object.values(state.ui.showEditorOnLine).reduce((acc, curr) => acc || curr, false)
      const feedbackWritten = Object.entries(state.updatedFeedback.feedbackLines).length > 0
      return openEditor || feedbackWritten
    },
    isFeedbackCreation: state => {
      return !state.origFeedback['feedbackStageForUser'] ||
        state.origFeedback['feedbackStageForUser'] === 'feedback-creation'
    }
  },
  mutations: {
    [subNotesMut.SET_SUBMISSION]: function (state, submission) {
      state.submission = submission
    },
    [subNotesMut.SET_ORIG_FEEDBACK]: function (state, feedback) {
      if (feedback) {
        state.origFeedback = feedback
        state.hasOrigFeedback = true
      }
    },
    [subNotesMut.SET_SHOW_FEEDBACK]: function (state, val) {
      state.ui.showFeedback = val
    },
    [subNotesMut.UPDATE_FEEDBACK_LINE]: function (state, feedback) {
      Vue.set(state.updatedFeedback.feedbackLines, feedback.lineNo, feedback.comment)
    },
    [subNotesMut.UPDATE_FEEDBACK_SCORE]: function (state, score) {
      state.updatedFeedback.score = score
    },
    [subNotesMut.DELETE_FEEDBACK_LINE]: function (state, lineNo) {
      Vue.delete(state.updatedFeedback.feedbackLines, lineNo)
    },
    [subNotesMut.TOGGLE_EDITOR_ON_LINE]: function (state, {lineNo, comment}) {
      Vue.set(state.ui.selectedCommentOnLine, lineNo, comment)
      Vue.set(state.ui.showEditorOnLine, lineNo, !state.ui.showEditorOnLine[lineNo])
    },
    [subNotesMut.MARK_COMMENT_FOR_DELETION]: function (state, comment) {
      Vue.set(state.commentsMarkedForDeletion, comment.pk, comment)
    },
    [subNotesMut.UN_MARK_COMMENT_FOR_DELETION]: function (state, comment) {
      Vue.delete(state.commentsMarkedForDeletion, comment.pk)
    },
    [subNotesMut.RESET_UPDATED_FEEDBACK]: function (state) {
      state.updatedFeedback = initialState().updatedFeedback
    },
    [subNotesMut.RESET_STATE]: function (state) {
      Object.assign(state, initialState())
    }
  },
  actions: {
    deleteComments: async function ({state}) {
      return Promise.all(
        Object.values(state.commentsMarkedForDeletion).map(comment => {
          return api.deleteComment((comment as any))
        })
      )
    },
    submitFeedback: async function ({state, dispatch, getters}, {isFinal = false}) {
      let feedback = {
        isFinal: isFinal,
        ofSubmission: state.submission.pk
      }
      if (Object.keys(state.updatedFeedback.feedbackLines).length > 0) {
        feedback['feedbackLines'] = state.updatedFeedback.feedbackLines
      }
      if (state.origFeedback.score === null && state.updatedFeedback.score === null) {
        throw new Error('You need to give a score.')
      } else if (state.updatedFeedback.score !== null) {
        feedback['score'] = state.updatedFeedback.score
      }
      await dispatch('deleteComments')
      if (!state.hasOrigFeedback) {
        return api.submitFeedbackForAssignment({feedback})
      } else {
        feedback['pk']= state.origFeedback.pk
        return api.submitUpdatedFeedback({feedback})
      }
    }
  }
}

export default submissionNotes