import { getInstanceActionId } from "@shared/helper/pluginHelper"
import { i18nInstance } from "@racletteOrchestrator"
import { v4 as uuidv4 } from "uuid"
import { nanoid } from "nanoid"
import type { Plugin } from "vue"
import type {
  CustomStoreActionsPayload,
  PluginFrontendDefinition,
  RacletteStore,
} from "@racletteCore"
import type { PluginApi } from "../types"
import {
  pluginDataTypes,
  registeredPlugins,
  addPluginDataType,
  addPluginReadOperation,
  addOrchestratorPlugin,
} from "../state"
import {
  splitOperations,
  createPluginActionId,
  handleOperationTarget,
  prefixOperationTarget,
} from "../utils/operations"

export const installPluginData = (
  store: RacletteStore,
  log: PluginApi["$log"],
) => ({
  addDataType: () => log("addDataType not implemented"),
  addNodeDependency: (dependency: Plugin, options: Record<string, string>) => {
    log("addNodeDependency will collect dependency", { dependency, options })
    addOrchestratorPlugin({ dependency, options })
  },
  addStoreEffect: (effect: any) => {
    log("addStoreEffect will add effect", effect)
    store.effect(effect)
  },
  dispatchStore: (type: string, payload: any, options?: any) => {
    let dispatchObject = { type, payload }
    if (options) {
      dispatchObject = { ...dispatchObject, ...options }
    }
    log("dispatchStore will dispatch", dispatchObject)
    store.dispatch(dispatchObject)
  },
})

export const handlePluginOperations =
  (prefixedPluginName: string, log: PluginApi["$log"]) =>
  (
    actionIdOverride = "",
    orchestratorResultWrapper: any,
    instanceId: string,
    pluginData: PluginFrontendDefinition["data"] = {},
  ) => {
    const customStoreActions: Record<string, any> = {}
    for (let [dataTypeName, value] of Object.entries(pluginData)) {
      if (!dataTypeName) return log("no type for datatype given", value)

      if (!pluginDataTypes[dataTypeName]) {
        const { operations, ...pluginDataDefinition } = value
        addPluginDataType(dataTypeName, pluginDataDefinition)
      }

      const operations = value.operations || {}
      const { cudOperations, readOperations } = splitOperations(operations)

      const storeActionTypeStoreActions = (customStoreActions[dataTypeName] =
        {})
      // Handle write operations
      Object.entries(cudOperations).forEach(
        ([customOperationName, customOperation]) => {
          customOperation.dataTypeName = dataTypeName
          let actionIdBase = customOperation.actionId || customOperationName
          if (actionIdOverride.length) actionIdBase = actionIdOverride
          const actionId = createPluginActionId(dataTypeName, actionIdBase)
          // This is the id for the query of the handler itself
          let handlerId = nanoid(4)
          storeActionTypeStoreActions[customOperationName] = ({
            id,
            params = {},
            options = {},
          }: CustomStoreActionsPayload = {}) => {
            if (id?.length) handlerId = id
            const instanceActionId =
              getInstanceActionId(
                prefixedPluginName,
                instanceId,
                actionId,
                actionIdOverride,
              ) + `-${handlerId}`
            if (!params._id && options.immediate === true) {
              params._id = uuidv4()
            }
            return orchestratorResultWrapper.write(
              instanceActionId,
              customOperation,
              {
                params,
                options,
              },
            )
          }
        },
      )
      // Handle read operations
      Object.entries(readOperations).forEach(
        ([customOperationName, customOperation]) => {
          customOperation.dataTypeName = dataTypeName
          let actionIdBase = customOperation.actionId || customOperationName
          if (actionIdOverride.length) actionIdBase = actionIdOverride
          const actionId = createPluginActionId(dataTypeName, actionIdBase)
          // This is the id for the query of the handler itself
          let handlerId = nanoid(4)
          storeActionTypeStoreActions[customOperationName] = ({
            id,
            params = {},
            options = {},
          }: CustomStoreActionsPayload = {}) => {
            if (id?.length) handlerId = id
            const instanceActionId =
              getInstanceActionId(
                prefixedPluginName,
                instanceId,
                actionId,
                actionIdOverride,
              ) + `-${handlerId}`
            const method = customOperation.method || "get"
            let target = handleOperationTarget(customOperation.target, params)

            const targetPrefix = `remote/${method}`

            target = prefixOperationTarget(target, targetPrefix)

            const operationConfig = {
              ...customOperation,
              targetPrefix,
            }
            // todo remove this singleton and pass the operationConfig to the query state as we need it in the fetch handler, also if it is called by the query effect
            addPluginReadOperation(instanceActionId, operationConfig)
            return orchestratorResultWrapper.read(instanceActionId, target, {
              params,
              options,
            })
          }
        },
      )
    }
    return customStoreActions
  }

export interface TranslationUpdate {
  [locale: string]: {
    [key: string]: any
  }
}

const expandLocaleToAvailableVariants = (
  locale: string,
  availableLocales: string[],
): string[] => {
  // If exact match exists, return it
  if (availableLocales.includes(locale)) {
    return [locale]
  }

  // If it's a short locale (no dash), find all matching long locales
  if (!locale.includes("-")) {
    const matchingLocales = availableLocales.filter((availableLocale) =>
      availableLocale.startsWith(locale + "-"),
    )
    return matchingLocales.length > 0 ? matchingLocales : []
  }

  // If it's a long locale that doesn't exist, return empty array
  return []
}

export const updateTranslations = (
  pluginKey: string,
  newTranslations: TranslationUpdate,
  log: PluginApi["$log"],
) => {
  Object.entries(newTranslations).forEach(([locale, messages]) => {
    const availableLocales = i18nInstance.global.availableLocales
    const targetLocales = expandLocaleToAvailableVariants(
      locale,
      availableLocales,
    )

    if (targetLocales.length > 0) {
      targetLocales.forEach((targetLocale) => {
        const existingMessages =
          i18nInstance.global.getLocaleMessage(targetLocale)
        const wrappedI18n: Record<string, any> = {}
        wrappedI18n[pluginKey] = messages

        const mergedMessages = { ...existingMessages, ...wrappedI18n }
        log(
          `updateTranslations added i18n for ${locale} -> ${targetLocale}`,
          wrappedI18n,
        )
        i18nInstance.global.setLocaleMessage(targetLocale, mergedMessages)
      })
    } else {
      log(
        `updateTranslations: No matching locales found for ${locale}. Available: ${availableLocales.join(", ")}`,
      )
    }
  })
}
