import { createI18n, useI18n } from "vue-i18n"
import type { I18n } from "vue-i18n"
import { createVueI18nAdapter } from "vuetify/locale/adapters/vue-i18n"
import defaultDateTimeFormats from "../i18n/dateTimeFormats"
import defaultNumberFormats from "../i18n/numberFormats"
import configService from "@racletteCore/lib/configService"
import logger from "@racletteCore/lib/logger"
import { mergeDeepRight } from "ramda"
import { $store } from "@racletteCore"

// Types
type LocaleFileImports = Record<string, () => Promise<any>>
type Messages = Record<string, any>
type DateLocales = Record<string, any>
type SectionPriorities = Record<string, number>

interface I18nInstance {
  i18nInstance: I18n
  instanceAdapter: { adapter: ReturnType<typeof createVueI18nAdapter> } | null
  dateLocale: DateLocales
}

interface LocaleSection {
  section: string
  content: any
  priority: number
}

// File imports using Vite's glob
const i18nFiles: LocaleFileImports = import.meta.glob(
  ["/src/orchestrator/i18n/**/*.json"],
  { eager: false },
)

const vuetifyLocaleFiles: LocaleFileImports = import.meta.glob(
  ["/node_modules/vuetify/lib/locale/*.js"],
  { eager: false },
)

const dateFnsLocaleFiles: LocaleFileImports = import.meta.glob(
  ["/node_modules/date-fns/locale/*.js"],
  { eager: false },
)

// Global state
let i18nInstance: I18n | null = null
let instanceAdapter: { adapter: any } | null = null
let dateLocale: DateLocales = {}
let messages: Messages = {}

// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================

const extractLocaleFromPath = (filePath: string): string => {
  const fileName = filePath.split("/").pop()!
  return fileName.replace(/\.(json|js)$/, "")
}

const getLanguageCode = (locale: string): string => {
  return locale.split("-")[0]
}

const getAvailableLocales = (files: LocaleFileImports): string[] => {
  return Object.keys(files).map(extractLocaleFromPath)
}

const filterValidDateFnsLocales = (locales: string[]): string[] => {
  return locales.filter(
    (locale) => !locale.includes("cdn") && locale !== "types",
  )
}

const createFormatsForAllLocales = (
  locales: string[],
  defaultFormats: any,
): Record<string, any> => {
  return locales.reduce(
    (formats, locale) => {
      formats[locale] = defaultFormats.EU
      return formats
    },
    {} as Record<string, any>,
  )
}

// ============================================================================
// LOCALE ENRICHMENT (FALLBACK CHAIN)
// ============================================================================

class LocaleEnrichment {
  private requestedLocales: string[]
  private availableLocales: string[]
  private fallbackLocale: string

  constructor(
    requestedLocales: string[],
    availableLocales: string[],
    fallbackLocale: string,
  ) {
    this.requestedLocales = requestedLocales
    this.availableLocales = availableLocales
    this.fallbackLocale = fallbackLocale
  }

  private buildFallbackChain(targetLocale: string): string[] {
    const chain: string[] = []
    const languageCode = getLanguageCode(targetLocale)

    // 1. Try exact match
    if (this.availableLocales.includes(targetLocale)) {
      chain.push(targetLocale)
    }

    // 2. Try same language, different region
    const sameLanguageLocales = this.availableLocales.filter(
      (locale) =>
        locale !== targetLocale &&
        getLanguageCode(locale) === languageCode &&
        locale.includes("-"),
    )
    chain.push(...sameLanguageLocales)

    // 3. Try language code only
    if (
      this.availableLocales.includes(languageCode) &&
      !chain.includes(languageCode)
    ) {
      chain.push(languageCode)
    }

    return chain
  }

  createEnrichmentMap(): Record<string, string[]> {
    const enrichmentMap: Record<string, string[]> = {}

    for (const locale of this.requestedLocales) {
      let fallbackChain = this.buildFallbackChain(locale)

      // Add fallback locale if nothing found
      if (
        fallbackChain.length === 0 &&
        this.availableLocales.includes(this.fallbackLocale)
      ) {
        fallbackChain = [this.fallbackLocale]
      }

      if (fallbackChain.length > 0) {
        enrichmentMap[locale] = fallbackChain
        logger.orchestrator(`Fallback chain for ${locale}:`, fallbackChain)
      }
    }

    return enrichmentMap
  }
}

// ============================================================================
// MESSAGE LOADING AND PROCESSING
// ============================================================================

class MessageProcessor {
  private priorities: SectionPriorities

  constructor(priorities: SectionPriorities) {
    this.priorities = priorities
  }

  private extractSectionFromPath(filePath: string): string {
    const pathParts = filePath.split("/")
    const i18nIndex = pathParts.findIndex((part) => part === "i18n")
    const folderPath = pathParts.slice(i18nIndex + 1, -1).join("/")
    return folderPath || "."
  }

  private async loadI18nFiles(
    files: LocaleFileImports,
  ): Promise<Record<string, LocaleSection[]>> {
    const sectionsByLocale: Record<string, LocaleSection[]> = {}

    for (const [filePath, importFn] of Object.entries(files)) {
      try {
        const module = await importFn()
        const content = module.default || module
        const locale = extractLocaleFromPath(filePath)
        const section = this.extractSectionFromPath(filePath)
        const priority = this.priorities[section]

        if (priority === undefined) {
          logger.warning(
            `Section "${section}" for locale "${locale}" has no priority - skipping`,
          )
          continue
        }

        if (!sectionsByLocale[locale]) {
          sectionsByLocale[locale] = []
        }

        sectionsByLocale[locale].push({ section, content, priority })
      } catch (error) {
        logger.error(`Failed to load i18n file ${filePath}:`, error)
      }
    }

    return sectionsByLocale
  }

  private mergeSectionsByPriority(
    sectionsByLocale: Record<string, LocaleSection[]>,
  ): Record<string, any> {
    const mergedByLocale: Record<string, any> = {}

    for (const [locale, sections] of Object.entries(sectionsByLocale)) {
      // Sort by priority (highest number = lowest priority, so merge first)
      const sortedSections = sections.sort((a, b) => b.priority - a.priority)

      // Merge from lowest to highest priority
      mergedByLocale[locale] = sortedSections.reduce((merged, { content }) => {
        return mergeDeepRight(merged, content || {})
      }, {})
    }

    return mergedByLocale
  }

  private enrichWithFallbacks(
    enrichmentMap: Record<string, string[]>,
    mergedByLocale: Record<string, any>,
  ): Messages {
    const enrichedMessages: Messages = {}

    for (const [requestedLocale, fallbackChain] of Object.entries(
      enrichmentMap,
    )) {
      let localeMessages = {}

      // Merge in reverse order (lowest priority first)
      const reversedChain = [...fallbackChain].reverse()
      for (const sourceLocale of reversedChain) {
        if (mergedByLocale[sourceLocale]) {
          localeMessages = mergeDeepRight(
            localeMessages,
            mergedByLocale[sourceLocale],
          )
          logger.orchestrator(`Merged ${sourceLocale} into ${requestedLocale}`)
        }
      }

      enrichedMessages[requestedLocale] = localeMessages
    }

    return enrichedMessages
  }

  async processMessages(
    files: LocaleFileImports,
    enrichmentMap: Record<string, string[]>,
  ): Promise<Messages> {
    const sectionsByLocale = await this.loadI18nFiles(files)
    const mergedByLocale = this.mergeSectionsByPriority(sectionsByLocale)
    return this.enrichWithFallbacks(enrichmentMap, mergedByLocale)
  }
}

// ============================================================================
// VUETIFY LOCALE LOADER
// ============================================================================

class VuetifyLocaleLoader {
  async loadVuetifyLocales(
    files: LocaleFileImports,
    requestedLocales: string[],
    messages: Messages,
  ): Promise<void> {
    for (const [filePath, importFn] of Object.entries(files)) {
      try {
        const module = await importFn()
        const vuetifyLocale = module.default || module
        const shortLocale = extractLocaleFromPath(filePath)

        // Find matching long locales
        const matchingLocales = requestedLocales.filter(
          (locale) => getLanguageCode(locale) === shortLocale,
        )

        for (const locale of matchingLocales) {
          if (messages[locale]) {
            messages[locale].$vuetify = vuetifyLocale
          }
        }
      } catch (error) {
        logger.error(`Failed to load Vuetify locale ${filePath}:`, error)
      }
    }
  }
}

// ============================================================================
// DATE-FNS LOCALE LOADER
// ============================================================================

class DateFnsLocaleLoader {
  private findBestDateFnsLocale(
    requestedLocale: string,
    availableLocales: string[],
  ): string | null {
    // Try exact match
    if (availableLocales.includes(requestedLocale)) {
      return requestedLocale
    }

    // Try language code
    const languageCode = getLanguageCode(requestedLocale)
    if (availableLocales.includes(languageCode)) {
      return languageCode
    }

    // Fallback to en-GB (English with European formatting)
    if (availableLocales.includes("en-GB")) {
      return "en-GB"
    }

    // Final fallback to en
    if (availableLocales.includes("en")) {
      return "en"
    }

    return null
  }

  async loadDateFnsLocales(requestedLocales: string[]): Promise<DateLocales> {
    const availableDateFnsLocales = filterValidDateFnsLocales(
      getAvailableLocales(dateFnsLocaleFiles),
    )

    // Create mapping of requested locale -> best available locale
    const localeMapping: Record<string, string> = {}
    for (const requestedLocale of requestedLocales) {
      const bestMatch = this.findBestDateFnsLocale(
        requestedLocale,
        availableDateFnsLocales,
      )
      if (bestMatch) {
        localeMapping[requestedLocale] = bestMatch
      }
    }

    // Load only the needed locale files
    const neededLocales = [...new Set(Object.values(localeMapping))]
    const dateLocales: DateLocales = {}

    for (const neededLocale of neededLocales) {
      const filePath = Object.keys(dateFnsLocaleFiles).find(
        (path) => extractLocaleFromPath(path) === neededLocale,
      )

      if (filePath) {
        try {
          const module = await dateFnsLocaleFiles[filePath]()
          const dateFnsLocale = module.default || module

          // Map this loaded locale to all requesting locales
          for (const [requestedLocale, targetLocale] of Object.entries(
            localeMapping,
          )) {
            if (targetLocale === neededLocale) {
              dateLocales[requestedLocale] = dateFnsLocale
            }
          }
        } catch (error) {
          logger.error(`Failed to load date-fns locale ${filePath}:`, error)
        }
      }
    }

    return dateLocales
  }
}

// ============================================================================
// FILE FILTERING
// ============================================================================

class FileFilter {
  static filterByLocales(targetLocales: string[]) {
    return (files: LocaleFileImports): LocaleFileImports => {
      const filtered: LocaleFileImports = {}

      for (const [filePath, importFn] of Object.entries(files)) {
        const fileLocale = extractLocaleFromPath(filePath)
        if (targetLocales.includes(fileLocale)) {
          filtered[filePath] = importFn
        }
      }

      return filtered
    }
  }
}

// ============================================================================
// MAIN ORCHESTRATOR
// ============================================================================

class I18nOrchestrator {
  private fallbackLocale = "en-EU"

  private async determineInitialLocale(
    availableLocales: string[],
  ): Promise<string> {
    const userLanguage = localStorage.getItem("user-language")
    const browserLocale = userLanguage || navigator.language
    let initialLocale = this.fallbackLocale

    // Use the same fallback logic as message loading
    const enrichment = new LocaleEnrichment(
      [browserLocale],
      availableLocales,
      this.fallbackLocale,
    )
    const enrichmentMap = enrichment.createEnrichmentMap()

    if (
      enrichmentMap[browserLocale] &&
      enrichmentMap[browserLocale].length > 0
    ) {
      initialLocale = enrichmentMap[browserLocale][0] // Take the first (best) match
      logger.orchestrator(`Initial locale: ${browserLocale} → ${initialLocale}`)
    } else {
      logger.orchestrator(
        `No match found for ${browserLocale}, using fallback: ${initialLocale}`,
      )
    }

    await $store.dispatch({
      type: "ui/update",
      payload: { locale: initialLocale },
    })

    return initialLocale
  }

  async initialize(): Promise<I18nInstance> {
    const { locales, priorities } = configService.getI18nConfig()
    const availableLocales = getAvailableLocales(i18nFiles)

    logger.orchestrator("Configured locales:", locales)
    logger.orchestrator("Available locale files:", availableLocales)

    // Create enrichment map for fallback chains
    const enrichment = new LocaleEnrichment(
      locales,
      availableLocales,
      this.fallbackLocale,
    )
    const enrichmentMap = enrichment.createEnrichmentMap()

    // Get all locales we need to load (including fallbacks)
    const allLocalesToLoad = new Set<string>()
    Object.values(enrichmentMap).forEach((fallbackChain) => {
      fallbackChain.forEach((locale) => allLocalesToLoad.add(locale))
    })

    // Load messages, Vuetify locales, and date-fns locales concurrently
    const [loadedMessages, loadedDateLocales] = await Promise.all([
      this.loadMessages(
        Array.from(allLocalesToLoad),
        enrichmentMap,
        priorities,
        locales,
      ),
      new DateFnsLocaleLoader().loadDateFnsLocales(locales),
    ])

    messages = loadedMessages
    dateLocale = loadedDateLocales

    logger.orchestrator("Loaded messages:", Object.keys(messages))
    logger.orchestrator("Loaded date locales:", Object.keys(dateLocale))

    // Create i18n instance
    const initialLocale = await this.determineInitialLocale(
      Object.keys(messages),
    )
    i18nInstance = createI18n({
      legacy: false,
      globalInjection: true,
      locale: initialLocale,
      fallbackLocale: this.fallbackLocale,
      messages,
      datetimeFormats: createFormatsForAllLocales(
        locales,
        defaultDateTimeFormats,
      ),
      numberFormats: createFormatsForAllLocales(locales, defaultNumberFormats),
    })

    instanceAdapter = {
      adapter: createVueI18nAdapter({ i18n: i18nInstance, useI18n }),
    }

    return { i18nInstance, instanceAdapter, dateLocale }
  }

  private async loadMessages(
    localesToLoad: string[],
    enrichmentMap: Record<string, string[]>,
    priorities: SectionPriorities,
    requestedLocales: string[],
  ): Promise<Messages> {
    // Filter i18n files and process messages
    const filteredI18nFiles =
      FileFilter.filterByLocales(localesToLoad)(i18nFiles)
    const messageProcessor = new MessageProcessor(priorities)
    const messages = await messageProcessor.processMessages(
      filteredI18nFiles,
      enrichmentMap,
    )

    // Load Vuetify locales
    const shortLocales = requestedLocales.map(getLanguageCode)
    const filteredVuetifyFiles =
      FileFilter.filterByLocales(shortLocales)(vuetifyLocaleFiles)
    const vuetifyLoader = new VuetifyLocaleLoader()
    await vuetifyLoader.loadVuetifyLocales(
      filteredVuetifyFiles,
      requestedLocales,
      messages,
    )

    return messages
  }
}

// ============================================================================
// PUBLIC API
// ============================================================================

const initI18n = async (): Promise<I18nInstance> => {
  if (!i18nInstance) {
    const orchestrator = new I18nOrchestrator()
    return await orchestrator.initialize()
  }
  return { i18nInstance, instanceAdapter, dateLocale }
}

export default initI18n
export { i18nInstance, instanceAdapter, dateLocale, initI18n }
