import type {
  ApplicationFormDto,
  ApplicationFormSnapshotDto,
  FormElementDto,
  FormElementSnapshotDto,
  FormOptionDto
} from '~~/.api-client'
import type { FormDiff, ElementModification, OptionChange, OptionModification } from '~~/types/formDiff'

export function compareApplicationForms(
  current: ApplicationFormDto,
  versionSnapshot: ApplicationFormSnapshotDto
): FormDiff {
  const diff: FormDiff = {
    elementsAdded: [],
    elementsRemoved: [],
    elementsModified: []
  }

  const currentElements = flattenFormElements(current)
  const versionElements = flattenSnapshotElements(versionSnapshot)

  const currentElementsMap = new Map(
    currentElements.map((el, idx) => [
      createElementKey(el.element, idx),
      { element: el.element, index: idx, sectionTitle: el.sectionTitle }
    ])
  )
  const versionElementsMap = new Map(
    versionElements.map((el, idx) => [
      createSnapshotElementKey(el.element, idx),
      { element: el.element, index: idx, sectionTitle: el.sectionTitle }
    ])
  )

  for (const [key, currentData] of currentElementsMap) {
    if (!versionElementsMap.has(key)) {
      diff.elementsAdded.push({
        sectionTitle: currentData.sectionTitle,
        title: currentData.element.title,
        type: currentData.element.type,
        position: currentData.index
      })
    } else {
      const versionData = versionElementsMap.get(key)!
      const modifications = compareElements(
        currentData.element,
        versionData.element,
        currentData.sectionTitle,
        currentData.index
      )
      if (modifications) {
        diff.elementsModified.push(modifications)
      }
    }
  }

  for (const [key, versionData] of versionElementsMap) {
    if (!currentElementsMap.has(key)) {
      diff.elementsRemoved.push({
        sectionTitle: versionData.sectionTitle,
        title: versionData.element.title,
        type: versionData.element.type,
        position: versionData.index
      })
    }
  }

  return diff
}

function flattenFormElements(form: ApplicationFormDto): Array<{ element: FormElementDto; sectionTitle: string }> {
  const elements: Array<{ element: FormElementDto; sectionTitle: string }> = []
  for (const section of form.formElementSections) {
    for (const element of section.formElements) {
      elements.push({ element, sectionTitle: section.title })
    }
  }
  return elements
}

function flattenSnapshotElements(
  snapshot: ApplicationFormSnapshotDto
): Array<{ element: FormElementSnapshotDto; sectionTitle: string }> {
  const elements: Array<{ element: FormElementSnapshotDto; sectionTitle: string }> = []
  for (const section of snapshot.sections) {
    for (const element of section.elements) {
      elements.push({ element, sectionTitle: section.title })
    }
  }
  return elements
}

function createElementKey(element: FormElementDto, index: number): string {
  return `${index}-${element.type}-${element.title || 'untitled'}`
}

function createSnapshotElementKey(element: FormElementSnapshotDto, index: number): string {
  return `${index}-${element.type}-${element.title || 'untitled'}`
}

function compareElements(
  current: FormElementDto,
  version: FormElementSnapshotDto,
  sectionTitle: string,
  position: number
): ElementModification | null {
  const optionsDiff = compareOptions(current.options, version.options)
  
  if (optionsDiff.added.length > 0 || optionsDiff.removed.length > 0 || optionsDiff.modified.length > 0) {
    return {
      sectionTitle,
      position,
      optionsAdded: optionsDiff.added,
      optionsRemoved: optionsDiff.removed,
      optionsModified: optionsDiff.modified
    }
  }

  return null
}

function compareOptions(
  currentOptions: FormOptionDto[],
  versionOptions: FormOptionDto[]
): { added: OptionChange[]; removed: OptionChange[]; modified: OptionModification[] } {
  const result = {
    added: [] as OptionChange[],
    removed: [] as OptionChange[],
    modified: [] as OptionModification[]
  }

  const currentMap = new Map(currentOptions.map((opt) => [opt.value, opt]))
  const versionMap = new Map(versionOptions.map((opt) => [opt.value, opt]))

  for (const [value, currentOpt] of currentMap) {
    if (!versionMap.has(value)) {
      result.added.push({
        value: currentOpt.value,
        label: currentOpt.label
      })
    } else {
      const versionOpt = versionMap.get(value)!
      if (currentOpt.label !== versionOpt.label) {
        result.modified.push({
          value,
          labelChanged: { from: versionOpt.label, to: currentOpt.label }
        })
      }
    }
  }

  for (const [value, versionOpt] of versionMap) {
    if (!currentMap.has(value)) {
      result.removed.push({
        value: versionOpt.value,
        label: versionOpt.label
      })
    }
  }

  return result
}
