import { single_changeRelationshipToAnotherObject, many_changeRelationshipToAnotherObject } from "src/scripts/databaseManager/relationshipManager"
import { I_OpenedDocument, I_ShortenedDocument } from "src/interfaces/I_OpenedDocument"
import { I_Blueprint, I_ExtraFields } from "src/interfaces/I_Blueprint"
import PouchDB from "pouchdb"
import { Loading, QSpinnerGears, extend } from "quasar"

/**
 * Saves the given project and handles all the needed procedures
 */
export const saveDocument = async (
  document: I_OpenedDocument,
  allOpenedDocuments: I_OpenedDocument[],
  allDocuments: I_ShortenedDocument[],
  editModeAfterSave: boolean | null,
  vueInstance: any,
  disableLoading = false
) => {
  const loadingSetup = {
    message: "<h4>Saving document...</h4>",
    spinnerColor: "primary",
    messageColor: "cultured",
    spinnerSize: 120,
    backgroundColor: "dark",
    // @ts-ignore
    spinner: QSpinnerGears
  }

  // Time for cases when people want to auto-generate A LOT of documents
  const saveTimeout = setTimeout(() => {
    if (!disableLoading) {
    // @ts-ignore
      Loading.show(loadingSetup)
    }
  }, 750)

  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  const currentBlueprint: {extraFields: I_ExtraFields[]} = vueInstance.SGET_blueprint(document.type)
  window.FA_dbs[document.type] = new PouchDB(document.type)

  let currentDocument = false as unknown as I_OpenedDocument
  try {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-call
    currentDocument = await window.FA_dbs[document.type].get(document._id)
  }
  catch (error) {
    // console.log("Creating new document")
  }

  let documentCopy = {} as unknown as I_OpenedDocument
  if (currentDocument) {
    documentCopy = extend(true, {}, document)
    documentCopy._rev = currentDocument?._rev
  }
  else {
    documentCopy = extend(true, {}, document)
  }

  allOpenedDocuments = allOpenedDocuments.filter(doc => doc._id !== document._id)

  const listOfNewDocuments: {
    name: string
    id: string
    type: string
  }[] = []

  // Handle single document autogeneration
  const single_relationshipTypesAutoGeneration = ["singleToNoneRelationship", "singleToSingleRelationship", "singleToManyRelationship"]
  const single_allRelationshipFieldsAutoGeneration = documentCopy.extraFields.filter(field => {
    const currentField = currentBlueprint.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
    return (currentField && single_relationshipTypesAutoGeneration.includes(currentField.type))
  })

  // Update single fields autogeneration
  for (const field of single_allRelationshipFieldsAutoGeneration) {
    const fieldValue = field.value?.value

    if (fieldValue?.isAutoGenerated) {
      // Check if the document already exists
      const alreadyCreated = listOfNewDocuments.find(e => {
        return (e.name === fieldValue.label && e.type === fieldValue.type)
      })

      // If new document
      if (!alreadyCreated) {
        listOfNewDocuments.push({
          name: fieldValue.label,
          id: fieldValue.id,
          type: fieldValue.type
        })

        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
        const pairedBlueprint: I_Blueprint = vueInstance.SGET_blueprint(fieldValue.type)

        const newDocument = {
          bgColor: undefined,
          color: undefined,
          extraFields: [

            {
              id: "name",
              value: fieldValue.label
            },
            {
              id: "parentDoc",
              value: ""
            },
            {
              id: "documentColor",
              value: ""
            },
            {
              id: "documentBackgroundColor",
              value: ""
            },
            {
              id: "finishedSwitch",
              value: ""
            },
            {
              id: "minorSwitch",
              value: ""
            },
            {
              id: "deadSwitch",
              value: ""
            },
            {
              id: "categorySwitch",
              value: ""
            },
            {
              id: "order",
              value: ""
            },
            {
              id: "tags",
              value: []
            },
            {
              id: "categoryDescription",
              value: ""
            },
            {
              id: "otherNames",
              value: []
            }
          ],
          hierarchicalPath: pairedBlueprint.namePlural,
          icon: pairedBlueprint.icon,
          id: fieldValue.id,
          type: fieldValue.type,
          // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
          url: `/project/display-content/${fieldValue.type}/${fieldValue.id}`,
          _id: fieldValue.id
        }

        // @ts-ignore
        window.FA_dbs[newDocument.type] = new PouchDB(newDocument.type)

        // @ts-ignore
        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
        await window.FA_dbs[newDocument.type].put(newDocument)

        // eslint-disable-next-line @typescript-eslint/no-unsafe-call
        vueInstance.SSET_addDocument({ doc: vueInstance.mapShortDocument(newDocument, vueInstance.SGET_allDocumentsByType(newDocument.type).docs) })
      }
      // If already created document
      else {
        fieldValue.id = alreadyCreated.id
        fieldValue._id = alreadyCreated.id
      }

      delete (fieldValue.isAutoGenerated)
    }
  }

  // Handle relationship single fields
  const single_relationshipTypes = ["singleToSingleRelationship", "singleToManyRelationship"]
  const single_allRelationshipFields = documentCopy.extraFields.filter(field => {
    const currentField = currentBlueprint.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
    return (currentField && single_relationshipTypes.includes(currentField.type))
  })

  // Update single fields
  for (const field of single_allRelationshipFields) {
    const single_updatedDocuments: I_OpenedDocument[] = await single_changeRelationshipToAnotherObject(field, documentCopy, currentDocument)

    const pairedFieldID = currentBlueprint.extraFields.find(e => e.id === field.id)?.relationshipSettings?.connectedField

    const filteredOpenedDocuments = single_updatedDocuments.filter(doc => {
      return allOpenedDocuments.find(subDoc => {
        return subDoc._id === doc._id
      })
    })

    const filteredallDocuments = single_updatedDocuments.filter(doc => {
      return allDocuments.find(subDoc => {
        return subDoc._id === doc._id
      })
    })

    // Update the particular field in each currently opened document
    filteredOpenedDocuments.forEach(doc => {
      const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)

      if (toUpdateIndex) {
        const matchingDoc = allOpenedDocuments.find(subDoc => subDoc._id === doc._id)
        if (matchingDoc) {
          matchingDoc.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
        }
      }
    })

    // Update the particular field in each all document
    filteredallDocuments.forEach(doc => {
      const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)

      if (toUpdateIndex) {
        const matchingDoc = allDocuments.find(subDoc => subDoc._id === doc._id)
        if (matchingDoc) {
          matchingDoc.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
        }
      }
    })
  }

  // Handle relationship many fields
  const many_relationshipTypes = ["manyToSingleRelationship", "manyToManyRelationship"]
  const many_allRelationshipFields = documentCopy.extraFields.filter(field => {
    const currentField = currentBlueprint.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
    return (currentField && many_relationshipTypes.includes(currentField.type))
  })

  // Handle many document autogeneration
  const many_relationshipTypesAutoGeneration = ["manyToNoneRelationship", "manyToSingleRelationship", "manyToManyRelationship"]
  const many_allRelationshipFieldsAutoGeneration = documentCopy.extraFields.filter(field => {
    const currentField = currentBlueprint.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
    return (currentField && many_relationshipTypesAutoGeneration.includes(currentField.type))
  })

  // Update many fields autogeneration
  for (const field of many_allRelationshipFieldsAutoGeneration) {
    const fieldArray = field.value?.value

    if (Array.isArray(fieldArray)) {
      for (const fieldValue of fieldArray) {
        if (fieldValue?.isAutoGenerated) {
          // Check if the document already exists
          const alreadyCreated = listOfNewDocuments.find(e => {
            return (e.name === fieldValue.label && e.type === fieldValue.type)
          })

          // If new document
          if (!alreadyCreated) {
            listOfNewDocuments.push({
              name: fieldValue.label,
              id: fieldValue.id,
              type: fieldValue.type
            })

            // eslint-disable-next-line @typescript-eslint/no-unsafe-call
            const pairedBlueprint: I_Blueprint = vueInstance.SGET_blueprint(fieldValue.type)

            const newDocument = {
              bgColor: undefined,
              color: undefined,
              extraFields: [

                {
                  id: "name",
                  value: fieldValue.label
                },
                {
                  id: "parentDoc",
                  value: ""
                },
                {
                  id: "documentColor",
                  value: ""
                },
                {
                  id: "documentBackgroundColor",
                  value: ""
                },
                {
                  id: "finishedSwitch",
                  value: ""
                },
                {
                  id: "minorSwitch",
                  value: ""
                },
                {
                  id: "deadSwitch",
                  value: ""
                },
                {
                  id: "categorySwitch",
                  value: ""
                },
                {
                  id: "order",
                  value: ""
                },
                {
                  id: "tags",
                  value: []
                },
                {
                  id: "categoryDescription",
                  value: ""
                }
              ],
              hierarchicalPath: pairedBlueprint.namePlural,
              icon: pairedBlueprint.icon,
              id: fieldValue.id,
              type: fieldValue.type,
              // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
              url: `/project/display-content/${fieldValue.type}/${fieldValue.id}`,
              _id: fieldValue.id
            }

            // @ts-ignore
            window.FA_dbs[newDocument.type] = new PouchDB(newDocument.type)

            // @ts-ignore
            // eslint-disable-next-line @typescript-eslint/no-unsafe-call
            await window.FA_dbs[newDocument.type].put(newDocument)

            // eslint-disable-next-line @typescript-eslint/no-unsafe-call
            vueInstance.SSET_addDocument({ doc: vueInstance.mapShortDocument(newDocument, vueInstance.SGET_allDocumentsByType(newDocument.type).docs) })
          }
          // If already created document
          else {
            fieldValue.id = alreadyCreated.id
            fieldValue._id = alreadyCreated.id
          }

          delete (fieldValue.isAutoGenerated)
        }
      }
    }
  }

  // Update many fields
  for (const field of many_allRelationshipFields) {
    const many_updatedDocuments: I_OpenedDocument[] = await many_changeRelationshipToAnotherObject(field, documentCopy, currentDocument)

    const pairedFieldID = currentBlueprint.extraFields.find(e => e.id === field.id)?.relationshipSettings?.connectedField

    const filteredDocuments = many_updatedDocuments.filter(doc => {
      return allOpenedDocuments.find(subDoc => {
        return subDoc._id === doc._id
      })
    })

    // Update the particular field in each currently opened document
    filteredDocuments.forEach(doc => {
      const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)

      if (toUpdateIndex) {
        const matchingDoc = allOpenedDocuments.find(subDoc => subDoc._id === doc._id)
        if (matchingDoc) {
          matchingDoc.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
        }
      }
    })

    const filteredallDocuments = many_updatedDocuments.filter(doc => {
      return allDocuments.find(subDoc => {
        return subDoc._id === doc._id
      })
    })

    // Update the particular field in each all document
    filteredallDocuments.forEach(doc => {
      const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)

      if (toUpdateIndex) {
        const matchingDoc = allDocuments.find(subDoc => subDoc._id === doc._id)
        if (matchingDoc) {
          matchingDoc.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
        }
      }
    })
  }

  documentCopy.isNew = false
  documentCopy.hasEdits = false
  documentCopy.editMode = false

  if (documentCopy.scrollDistance) {
    delete (documentCopy.scrollDistance)
  }

  // Save the document
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  await window.FA_dbs[document.type].put(documentCopy)

  // Set edit mode for frontend
  if (editModeAfterSave !== null) {
    documentCopy.editMode = editModeAfterSave
  }

  clearTimeout(saveTimeout)
  if (!disableLoading) {
    Loading.hide()
  }

  return { documentCopy, allOpenedDocuments }
}

export const addFieldToDocument = async (targetDocumentID: string, fieldID: string, blueprintID: string) => {
  const BlueprintsDB = new PouchDB("blueprints")
  const currentBlueprint: {extraFields: I_ExtraFields[], _id: string} = await BlueprintsDB.get(blueprintID)

  window.FA_dbs[currentBlueprint._id] = new PouchDB(currentBlueprint._id)
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  const targetDocument: { extraFields: any[]} = await window.FA_dbs[currentBlueprint._id].get(targetDocumentID)

  const newField = {
    id: fieldID,
    value: ""
  }

  targetDocument.extraFields.push(newField)

  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
  await window.FA_dbs[currentBlueprint._id].put(targetDocument)
}
