import type { IconName, OptionProps } from '@blueprintjs/core'
import notEmpty from '@inject/shared/utils/notEmpty'
import type {
  InjectInfo,
  LearningActivityInfo,
  Milestone,
  MilestoneName,
} from './indexeddb/types'
import {
  InjectType,
  LearningActivityType,
  MilestoneEventType,
} from './indexeddb/types'

export const LEARNING_ACTIVITY_TYPES = Object.values(LearningActivityType)

export const INJECT_TYPES = Object.values(InjectType)

export const getLearningActivityIcon = (
  activity: LearningActivityInfo
): IconName => {
  switch (activity.type) {
    case LearningActivityType.EMAIL:
      return 'envelope'
    case LearningActivityType.TOOL:
      return 'wrench'
    default:
      return 'tick-circle'
  }
}

export const getInjectIcon = (inject: InjectInfo): IconName => {
  switch (inject.type) {
    case InjectType.EMAIL:
      return 'envelope'
    case InjectType.QUESTIONNAIRE:
      return 'th-list'
    default:
      return 'clipboard'
  }
}

// expression builder
export const DEFAULT_OPTION: OptionProps = { value: 0, label: '+' }
export const OPENING_BRACKET: OptionProps = { value: -1, label: '(' }
export const CLOSING_BRACKET: OptionProps = { value: -2, label: ')' }
export const NOT: OptionProps = { value: -3, label: 'not' }
export const AND: OptionProps = { value: -4, label: 'and' }
export const OR: OptionProps = { value: -5, label: 'or' }
export const OPERATORS: OptionProps[] = [AND, OR]
export const ALL_OPERATORS: OptionProps[] = [
  CLOSING_BRACKET,
  OPENING_BRACKET,
  NOT,
  ...OPERATORS,
]

export const getBlockFromId = (id: number, variables: OptionProps[]) =>
  ALL_OPERATORS.find(operator => operator.value === id) ||
  variables.find(variable => variable.value === id)

export const getExpression = (expression: number[], variables: OptionProps[]) =>
  expression.map(id => getBlockFromId(id, variables)).filter(notEmpty)

export const getVariables = (milestones: MilestoneName[]) =>
  milestones.map(milestone => ({
    value: milestone.id,
    label: getMilestoneName(milestone),
  }))

export const getVariablesForOutput = (milestones: MilestoneName[]) =>
  milestones.map(milestone => ({
    value: milestone.id,
    label: getMilestoneNameForOutput(milestone),
  }))

export const getPrefixByMilestoneType = (type: MilestoneEventType) => {
  switch (type) {
    case MilestoneEventType.LEARNING_ACTIVITY:
      return 'la'
    case MilestoneEventType.INJECT:
      return 'i'
    case MilestoneEventType.TOOL:
      return 'tr'
    case MilestoneEventType.EMAIL:
      return 'et'
    default:
      return ''
  }
}

export const getMilestoneName = (milestone: MilestoneName) =>
  `${getPrefixByMilestoneType(milestone.type)}: ${milestone.name}`

export const getMilestoneNameForOutput = (milestone: MilestoneName) =>
  `${getPrefixByMilestoneType(milestone.type)}_${milestone.name.replace(/\s+/g, '_').replace(/[^A-Za-z0-9_]/g, '')}_${milestone.id}`

type ValidationResult = {
  isValid: boolean
  error: string
}

export const validateExpression = (
  expression: number[],
  variables: Milestone[]
): ValidationResult => {
  let balance = 0
  let lastBlock: number | undefined = undefined

  for (const block of expression) {
    if (block === OPENING_BRACKET.value) {
      balance++
      if (variables.find(value => value.id === lastBlock)) {
        return { isValid: false, error: 'Missing operator after variable' }
      }
      if (lastBlock === CLOSING_BRACKET.value) {
        return {
          isValid: false,
          error: 'Missing operator after closing bracket',
        }
      }
    } else if (block === CLOSING_BRACKET.value) {
      balance--
      if (balance < 0) {
        return { isValid: false, error: 'More closing brackets' }
      }
      if (lastBlock === OPENING_BRACKET.value) {
        return { isValid: false, error: 'Empty brackets' }
      }
      if (
        OPERATORS.find(operator => operator.value === lastBlock) ||
        lastBlock === NOT.value
      ) {
        return { isValid: false, error: 'Missing variable after operator' }
      }
    } else if (OPERATORS.find(operator => operator.value === block)) {
      if (!lastBlock)
        return { isValid: false, error: 'Cannot start with an operator' }
      if (OPERATORS.find(operator => operator.value === lastBlock)) {
        return { isValid: false, error: 'Two operators in a row' }
      }
      if (lastBlock === NOT.value) {
        return { isValid: false, error: 'Missing variable after NOT' }
      }
      if (lastBlock === OPENING_BRACKET.value) {
        return {
          isValid: false,
          error: 'Missing variable after opening bracket',
        }
      }
    } else if (variables.find(value => value.id === block)) {
      if (variables.find(value => value.id === lastBlock)) {
        return { isValid: false, error: 'Two variables in a row' }
      }
      if (lastBlock === CLOSING_BRACKET.value) {
        return {
          isValid: false,
          error: 'Missing operator after closing bracket',
        }
      }
    } else if (block === NOT.value) {
      if (lastBlock === CLOSING_BRACKET.value) {
        return {
          isValid: false,
          error: 'Missing operator after closing bracket',
        }
      }
      if (variables.find(value => value.id === lastBlock)) {
        return { isValid: false, error: 'Missing operator after variable' }
      }
    } else {
      if (expression.length === 1) return { isValid: true, error: '' }
      return { isValid: false, error: 'Missing variable' }
    }
    lastBlock = block
  }

  if (
    OPERATORS.find(operator => operator.value === lastBlock) ||
    lastBlock === NOT.value
  ) {
    return { isValid: false, error: 'Cannot end with operator' }
  } else if (balance > 0) {
    return { isValid: false, error: 'Missing closing bracket' }
  } else {
    return { isValid: true, error: '' }
  }
}
