import { byId, isDefined, isNotDefined } from '@typebot.io/lib'
import { Group, SessionState, VariableWithValue } from '@typebot.io/schemas'
import { upsertResult } from './queries/upsertResult'
import { VisitedEdge } from '@typebot.io/prisma'

export type NextGroup = {
  group?: Group
  newSessionState: SessionState
  visitedEdge?: VisitedEdge
}

export const getNextGroup =
  (state: SessionState) =>
  async (edgeId?: string): Promise<NextGroup> => {
    const nextEdge = state.typebotsQueue[0].typebot.edges.find(byId(edgeId))
    if (!nextEdge) {
      if (state.typebotsQueue.length > 1) {
        const nextEdgeId = state.typebotsQueue[0].edgeIdToTriggerWhenDone
        const isMergingWithParent = state.typebotsQueue[0].isMergingWithParent
        const currentResultId = state.typebotsQueue[0].resultId
        if (!isMergingWithParent && currentResultId)
          await upsertResult({
            resultId: currentResultId,
            typebot: state.typebotsQueue[0].typebot,
            isCompleted: true,
            hasStarted: state.typebotsQueue[0].answers.length > 0,
          })
        let newSessionState = {
          ...state,
          typebotsQueue: [
            {
              ...state.typebotsQueue[1],
              typebot: isMergingWithParent
                ? {
                    ...state.typebotsQueue[1].typebot,
                    variables: state.typebotsQueue[1].typebot.variables
                      .map((variable) => ({
                        ...variable,
                        value:
                          state.typebotsQueue[0].typebot.variables.find(
                            (v) => v.name === variable.name
                          )?.value ?? variable.value,
                      }))
                      .concat(
                        state.typebotsQueue[0].typebot.variables.filter(
                          (variable) =>
                            isDefined(variable.value) &&
                            isNotDefined(
                              state.typebotsQueue[1].typebot.variables.find(
                                (v) => v.name === variable.name
                              )
                            )
                        ) as VariableWithValue[]
                      ),
                  }
                : state.typebotsQueue[1].typebot,
              answers: isMergingWithParent
                ? [
                    ...state.typebotsQueue[1].answers.filter(
                      (incomingAnswer) =>
                        !state.typebotsQueue[0].answers.find(
                          (currentAnswer) =>
                            currentAnswer.key === incomingAnswer.key
                        )
                    ),
                    ...state.typebotsQueue[0].answers,
                  ]
                : state.typebotsQueue[1].answers,
            },
            ...state.typebotsQueue.slice(2),
          ],
        } satisfies SessionState
        if (state.progressMetadata)
          newSessionState.progressMetadata = {
            ...state.progressMetadata,
            totalAnswers:
              state.progressMetadata.totalAnswers +
              state.typebotsQueue[0].answers.length,
          }
        const nextGroup = await getNextGroup(newSessionState)(nextEdgeId)
        newSessionState = nextGroup.newSessionState
        if (!nextGroup)
          return {
            newSessionState,
          }
        return {
          ...nextGroup,
          newSessionState,
        }
      }
      return {
        newSessionState: state,
      }
    }
    const nextGroup = state.typebotsQueue[0].typebot.groups.find(
      byId(nextEdge.to.groupId)
    )
    if (!nextGroup)
      return {
        newSessionState: state,
      }
    const startBlockIndex = nextEdge.to.blockId
      ? nextGroup.blocks.findIndex(byId(nextEdge.to.blockId))
      : 0
    const currentVisitedEdgeIndex = (state.currentVisitedEdgeIndex ?? -1) + 1
    const resultId = state.typebotsQueue[0].resultId
    return {
      group: {
        ...nextGroup,
        blocks: nextGroup.blocks.slice(startBlockIndex),
      } as Group,
      newSessionState: {
        ...state,
        currentVisitedEdgeIndex,
      },
      visitedEdge: resultId
        ? {
            index: currentVisitedEdgeIndex,
            edgeId: nextEdge.id,
            resultId,
          }
        : undefined,
    }
  }
