import notEmpty from '@inject/shared/utils/notEmpty'
import type JSZip from 'jszip'
import { parse } from 'yaml'
import { CONCLUSION_CONDITIONS } from '../assets/pageContent/conclusion'
import { INTRODUCTION_CONDITIONS } from '../assets/pageContent/introduction'
import { db } from '../indexeddb/db'
import { addFile, addMarkdownContent } from '../indexeddb/operations'
import type { EditorConfig } from '../useEditorStorage'
import type { GitlabConfig } from '../useGitlabStorage'
import { parseChannels } from '../yaml/parse/channels'
import { parseConfig } from '../yaml/parse/config'
import { loadEmailAddresses } from '../yaml/parse/email'
import { loadInjectControls, loadInjects } from '../yaml/parse/injects'
import {
  extractMilestonesFromControls,
  filterEmailMilestones,
  filterToolMilestones,
} from '../yaml/parse/milestones'
import {
  addActivityIdsToActivityMilestones,
  addTypesToActivities,
  loadObjectives,
} from '../yaml/parse/objectives'
import {
  loadQuestionnaireControls,
  loadQuestionnaires,
} from '../yaml/parse/questionnaires'
import { loadToolResponseControls, loadTools } from '../yaml/parse/tools'
import type { MilestoneYaml } from '../yaml/types'
import {
  CHANNELS_FILE_NAME,
  CONFIG_FILE_NAME,
  CONTENT_FOLDER_NAME,
  EMAIL_FILE_NAME,
  FILES_FOLDER_NAME,
  INJECTS_FILE_NAME,
  MILESTONES_FILE_NAME,
  OBJECTIVES_FILE_NAME,
  QUESTIONNAIRES_FILE_NAME,
  TOOLS_FILE_NAME,
} from './utils'

const parseZipFile = async (zip: JSZip, fileName: string) => {
  const file = await zip.file(fileName)?.async('string')
  return parse(file || '')
}

export const loadEditorConfig = async (
  zip: JSZip,
  gitlabConfig?: GitlabConfig
): Promise<EditorConfig> => {
  const configYaml = await parseZipFile(zip, CONFIG_FILE_NAME)
  const parsedConfig = parseConfig(configYaml)

  const channelsYaml = await parseZipFile(zip, CHANNELS_FILE_NAME)
  const parsedChannels = parseChannels(channelsYaml)

  const config = {
    introChecked: INTRODUCTION_CONDITIONS.map(() => true),
    conclusionChecked: CONCLUSION_CONDITIONS.map(() => true),
    name: gitlabConfig?.project?.name || 'Definition',
    description: gitlabConfig?.project?.description || 'Description',
    trainee: 'Trainee',
    ...parsedConfig,
    ...parsedChannels,
  }

  return config
}

const preloadFilesToDb = async (zip: JSZip) => {
  const filesFolder = zip.folder(FILES_FOLDER_NAME)
  filesFolder?.forEach(async (path, entry) => {
    const fileContent = await entry.async('blob')
    await addFile({ name: path, blob: fileContent })
  })

  const contentFolder = zip.folder(CONTENT_FOLDER_NAME)
  contentFolder?.forEach(async (path, entry) => {
    const fileContent = await entry.async('string')
    await addMarkdownContent({ fileName: path, content: fileContent })
  })
}

export const loadDbData = async (zip: JSZip) => {
  await preloadFilesToDb(zip)

  const emailYaml = await parseZipFile(zip, EMAIL_FILE_NAME)
  const injectsYaml = await parseZipFile(zip, INJECTS_FILE_NAME)
  const milestonesYaml = await parseZipFile(zip, MILESTONES_FILE_NAME)
  const objectivesYaml = await parseZipFile(zip, OBJECTIVES_FILE_NAME)
  const questionnairesYaml = await parseZipFile(zip, QUESTIONNAIRES_FILE_NAME)
  const toolsYaml = await parseZipFile(zip, TOOLS_FILE_NAME)

  const activityMilestones = milestonesYaml.filter(
    (milestone: MilestoneYaml) => milestone.activity
  )

  // get tool responses that activate LA milestone
  const toolMilestones = toolsYaml
    ? filterToolMilestones(activityMilestones, toolsYaml)
    : []
  // get email templates that activate LA milestone
  const emailMilestones = emailYaml
    ? filterEmailMilestones(activityMilestones, emailYaml)
    : []
  // add channel type to activities
  const objectivesWithActivityTypes = addTypesToActivities(
    objectivesYaml,
    toolMilestones,
    emailMilestones
  )
  const activities = (await loadObjectives(objectivesWithActivityTypes)).flat(1)

  // get mapping: definition milestone name -> learning activity id + milestone id
  const activityMilestonesWithIds = addActivityIdsToActivityMilestones(
    activityMilestones,
    activities
  ).filter(notEmpty)

  /*
   * when adding email templates: add LA id AND get mapping milestone name - email template id
   * needs to happen before loading injects, so the email addresses are already stored
   */
  const emailControls = emailYaml
    ? (await loadEmailAddresses(activityMilestonesWithIds, emailYaml)).flat(1)
    : []

  // when adding tool responses: add LA id AND get mapping milestone name - tool response id + save milestone condition
  const toolControls = toolsYaml
    ? (await loadTools(activityMilestonesWithIds, toolsYaml)).flat(1)
    : []

  // when adding questionnaires: get mapping milestone name - inject id + save milestone condition
  const questionnaireControls = questionnairesYaml
    ? await loadQuestionnaires(questionnairesYaml)
    : []

  // when adding injects: get mapping milestone name - inject id + save milestone condition + group alternatives
  const injectControlGroups = (await loadInjects(injectsYaml)).filter(notEmpty)
  const injectControls = injectControlGroups.flat(1)

  // milestone from definition to milestone id
  const milestonesWithIds = extractMilestonesFromControls(
    toolControls,
    emailControls,
    injectControls,
    questionnaireControls
  )

  loadToolResponseControls(toolControls, milestonesWithIds)
  loadQuestionnaireControls(questionnaireControls, milestonesWithIds)
  loadInjectControls(injectControlGroups, milestonesWithIds)

  // get final milestone
  const finalMilestone: MilestoneYaml = milestonesYaml.find(
    (milestone: MilestoneYaml) => milestone.final
  )
  const finalMilestoneId = milestonesWithIds.find(
    milestone => milestone.name === finalMilestone.name
  )?.id

  // remove temporary content
  await db.markdownContents.clear()

  return finalMilestoneId
}
