import {
  addEmailInject,
  addInformationInject,
  addInjectControl,
  addInjectInfo,
  getEmailAddressByName,
  getMilestoneByTypeAndReferenceId,
} from '@/editor/indexeddb/operations'
import { InjectType, MilestoneEventType } from '@/editor/indexeddb/types'
import { AND, CLOSING_BRACKET, NOT, OPENING_BRACKET } from '@/editor/utils'
import type {
  InjectAlternativeEmailYaml,
  InjectAlternativeInfoYaml,
  InjectCategoryYaml,
  MappedInjectControl,
  MappedMilestone,
} from '../types'
import { extractFirstMilestone } from './milestones'
import {
  getContentFile,
  getContentText,
  getMilestoneCondition,
  loadOverlay,
} from './shared'

const loadInfoInject = async (
  injectCategory: InjectCategoryYaml,
  infoInject: InjectAlternativeInfoYaml,
  injectInfoId: number
) => {
  const file = await getContentFile(infoInject.content)
  const content = await getContentText(infoInject.content)

  await addInformationInject({
    injectInfoId,
    content,
    fileId: file?.id,
  })
  await loadOverlay(injectInfoId, infoInject.overlay)
  const milestone = await getMilestoneByTypeAndReferenceId(
    MilestoneEventType.INJECT,
    injectInfoId
  )

  return {
    id: injectInfoId,
    milestoneId: milestone?.id,
    milestoneName: extractFirstMilestone(
      infoInject.control?.activate_milestone
    ),
    milestoneCondition: infoInject.control?.milestone_condition,
    time: injectCategory.time,
    delay: injectCategory.delay,
  }
}

const loadEmailInject = async (
  injectCategory: InjectCategoryYaml,
  emailInject: InjectAlternativeEmailYaml,
  injectInfoId: number
) => {
  const file = await getContentFile(emailInject.content)
  const content = await getContentText(emailInject.content)
  const sender = await getEmailAddressByName(emailInject.sender)

  await addEmailInject({
    injectInfoId,
    emailAddressId: sender?.id || 0,
    subject: emailInject.subject,
    content,
    extraCopies: emailInject.extra_copies,
    fileId: file?.id,
  })
  await loadOverlay(injectInfoId, emailInject.overlay)
  const milestone = await getMilestoneByTypeAndReferenceId(
    MilestoneEventType.INJECT,
    injectInfoId
  )

  return {
    id: injectInfoId,
    milestoneId: milestone?.id,
    milestoneName: extractFirstMilestone(
      emailInject.control?.activate_milestone
    ),
    milestoneCondition: emailInject.control?.milestone_condition,
    time: injectCategory.time,
    delay: injectCategory.delay,
  }
}

export const loadInjects = async (injects: InjectCategoryYaml[]) =>
  await Promise.all(
    injects.map(async inject => {
      if (!inject.type || inject.type === 'info') {
        return await Promise.all(
          inject.alternatives.map(async alternative => {
            const id = await addInjectInfo({
              name: `${inject.name}${inject.alternatives.length > 1 ? ` - ${alternative.name}` : ''}`,
              type: InjectType.INFORMATION,
            })
            return await loadInfoInject(inject, alternative, id)
          })
        )
      }
      if (inject.type === 'email') {
        return await Promise.all(
          inject.alternatives.map(async alternative => {
            const id = await addInjectInfo({
              name: `${inject.name}${inject.alternatives.length > 1 ? ` - ${alternative.name}` : ''}`,
              type: InjectType.EMAIL,
            })
            return await loadEmailInject(
              inject,
              alternative as InjectAlternativeEmailYaml,
              id
            )
          })
        )
      }
    })
  )

export const loadInjectControls = (
  injectControlGroups: MappedInjectControl[][],
  milestonesWithIds: MappedMilestone[]
) => {
  injectControlGroups.forEach(controlGroup => {
    const milestoneIds = controlGroup
      .map(control => control.milestoneId)
      .filter(id => id !== undefined) as number[]
    controlGroup.map(async control => {
      await addInjectControl({
        injectInfoId: control.id,
        start: control.time,
        delay: control.delay,
        milestoneCondition: control.milestoneCondition
          ? getInjectMilestoneCondition(
              control.milestoneCondition,
              milestonesWithIds,
              milestoneIds,
              control.milestoneId
            )
          : [],
      })
    })
  })
}

const getInjectMilestoneCondition = (
  condition: string,
  milestonesWithIds: MappedMilestone[],
  groupMilestoneIds: number[],
  milestoneId?: number
) => {
  const originalCondition = getMilestoneCondition(condition, milestonesWithIds)
  // excluding other inject alternatives
  const exclusiveMilestoneIds = groupMilestoneIds.filter(
    id => id !== milestoneId
  )

  if (exclusiveMilestoneIds.length) {
    const originalConditionWithBrackets = originalCondition.length
      ? [
          Number(OPENING_BRACKET.value),
          ...originalCondition,
          Number(CLOSING_BRACKET.value),
        ]
      : []

    return [
      ...originalConditionWithBrackets,
      ...exclusiveMilestoneIds
        .map(id => [Number(AND.value), Number(NOT.value), id])
        .flat(1),
    ]
  }
  return originalCondition
}
