import type { Component } from "vue"
import { h } from "vue"
import type { WidgetDeclaration } from "@racletteOrchestrator"
import { registeredPlugins } from "@racletteCore/setup/plugin-system"
import logger from "@racletteCore/lib/logger"

export type WidgetInfo = {
  widgetName: string
  componentPath: string
  setupPath?: string
  componentLoader: () => Promise<Component>
  setupLoader?: () => Promise<{ details: WidgetDeclaration; config: any }>
}

interface WidgetSource {
  base: string
  components: Record<string, () => Promise<any>>
  setups: Record<string, () => Promise<any>>
}

// Define widget sources with their base paths
const widgetSources: WidgetSource[] = [
  {
    base: "/src/corePlugins",
    components: import.meta.glob(
      "/src/corePlugins/**/frontend/widgets/**/*Widget.vue",
      { eager: false },
    ),
    setups: import.meta.glob(
      "/src/corePlugins/**/frontend/widgets/**/setup.ts",
      {
        eager: false,
      },
    ),
  },
  {
    base: "/src/plugins",
    components: import.meta.glob(
      "/src/plugins/**/frontend/widgets/**/*Widget.vue",
      { eager: false },
    ),
    setups: import.meta.glob("/src/plugins/**/frontend/widgets/**/setup.ts", {
      eager: false,
    }),
  },
  {
    base: "/src/workbenchPlugins",
    components: import.meta.glob(
      "/src/workbenchPlugins/**/frontend/widgets/**/*Widget.vue",
      { eager: false },
    ),
    setups: import.meta.glob(
      "/src/workbenchPlugins/**/frontend/widgets/**/setup.ts",
      { eager: false },
    ),
  },
  {
    base: "/node_modules",
    components: import.meta.glob(
      [
        "/node_modules/**/frontend/widgets/**/*Widget.vue",
        "/node_modules/**/src/frontend/widgets/**/*Widget.vue",
      ],
      { eager: false },
    ),
    setups: import.meta.glob(
      [
        "/node_modules/**/frontend/widgets/**/setup.ts",
        "/node_modules/**/src/frontend/widgets/**/setup.ts",
      ],
      { eager: false },
    ),
  },
]

const widgetsByPath = new Map<string, WidgetInfo[]>()

// =====================================================
// WIDGET DISCOVERY
// =====================================================

const discoverWidgetsFromSource = (
  source: WidgetSource,
  sourceIndex: number,
) => {
  const componentPaths = Object.keys(source.components)

  logger.widgets("Discovering widgets from source", {
    base: source.base,
    componentCount: componentPaths.length,
    sourceIndex: sourceIndex + 1,
  })

  componentPaths.forEach((componentPath) => {
    const widgetInfo = processWidgetComponent(componentPath, source)
    if (widgetInfo) {
      storeWidgetInfo(widgetInfo)
    }
  })
}

const processWidgetComponent = (
  componentPath: string,
  source: WidgetSource,
): WidgetInfo | null => {
  const widgetName = extractWidgetName(componentPath)
  if (!widgetName) {
    logger.widgets("Could not extract widget name", { componentPath })
    return null
  }

  const frontendPath = extractPluginFrontendPath(componentPath, source.base)
  if (!frontendPath) {
    logger.widgets("Could not extract frontend path", { componentPath })
    return null
  }

  const setupPath = findSetupPath(componentPath, source)

  return {
    widgetName,
    componentPath,
    setupPath: setupPath || undefined,
    componentLoader: source.components[componentPath],
    setupLoader: setupPath ? source.setups[setupPath] : undefined,
  }
}

const storeWidgetInfo = (widget: WidgetInfo) => {
  const frontendPath = extractPluginFrontendPath(
    widget.componentPath,
    getSourceBase(widget.componentPath),
  )
  if (!frontendPath) return

  if (!widgetsByPath.has(frontendPath)) {
    widgetsByPath.set(frontendPath, [])
  }

  widgetsByPath.get(frontendPath)!.push(widget)
}

const getSourceBase = (componentPath: string): string => {
  return (
    widgetSources.find((source) => componentPath.startsWith(source.base))
      ?.base || ""
  )
}

const initializeWidgetDiscovery = () => {
  logger.widgets("Initializing widget discovery", {
    sourceCount: widgetSources.length,
  })

  widgetSources.forEach(discoverWidgetsFromSource)

  const totalWidgets = Array.from(widgetsByPath.values()).reduce(
    (sum, widgets) => sum + widgets.length,
    0,
  )

  logger.widgets("Widget discovery complete", {
    pluginPaths: widgetsByPath.size,
    totalWidgets,
    pluginSummary: Array.from(widgetsByPath.entries()).map(
      ([path, widgets]) => ({
        path,
        widgetCount: widgets.length,
        widgets: widgets.map((w) => w.widgetName),
      }),
    ),
  })
}

// =====================================================
// WIDGET LOADING
// =====================================================

const loadWidget = async (widgetInfo: WidgetInfo) => {
  try {
    const componentModule = await widgetInfo.componentLoader()
    const component = componentModule.default || componentModule

    const setup = await loadWidgetSetup(widgetInfo)

    return { component, ...setup }
  } catch (error) {
    logger.widgets("Failed to load widget", {
      widgetName: widgetInfo.widgetName,
      error: error instanceof Error ? error.message : String(error),
    })

    return {
      component: createErrorComponent(widgetInfo.widgetName),
    }
  }
}

const loadWidgetSetup = async (widgetInfo: WidgetInfo) => {
  if (!widgetInfo.setupLoader) {
    return { details: undefined, config: undefined }
  }

  try {
    const setupModule = await widgetInfo.setupLoader()
    return {
      details: setupModule.details,
      config: setupModule.config,
    }
  } catch (error) {
    logger.widgets("Failed to load widget setup", {
      widgetName: widgetInfo.widgetName,
      error: error instanceof Error ? error.message : String(error),
    })
    return { details: undefined, config: undefined }
  }
}

const createErrorComponent = (widgetName: string): Component => ({
  name: `${widgetName}Error`,
  setup: () => () =>
    h(
      "div",
      { class: "p-4 text-red-500 border border-red-300 rounded" },
      `Failed to load widget: ${widgetName}`,
    ),
})

// =====================================================
// PATH RESOLUTION HELPERS
// =====================================================

const extractWidgetName = (componentPath: string): string | null => {
  const match = componentPath.match(/\/([^/]+)Widget\.vue$/)
  return match ? match[1] : null
}

const extractPluginFrontendPath = (
  componentPath: string,
  base: string,
): string | null => {
  if (!componentPath.startsWith(base)) {
    return null
  }

  const frontendMatch = componentPath.match(/^(.+\/frontend)\/widgets\//)
  if (!frontendMatch) {
    logger.widgets(
      `🚨 Could not find frontend directory in path: ${componentPath}`,
    )
    return null
  }

  return frontendMatch[1]
}

const findSetupPath = (
  componentPath: string,
  source: WidgetSource,
): string | null => {
  const setupPath = componentPath.replace(/\/[^/]*Widget\.vue$/, "/setup.ts")
  return Object.keys(source.setups).includes(setupPath) ? setupPath : null
}

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

export const getPluginWidgets = (
  pluginFrontendPath: string,
): Record<string, any> => {
  const widgets = widgetsByPath.get(pluginFrontendPath) || []
  const result: Record<string, any> = {}

  widgets.forEach((widget) => {
    result[widget.widgetName] = {
      componentLoader: widget.componentLoader,
      setupLoader: widget.setupLoader,
      componentPath: widget.componentPath,
      setupPath: widget.setupPath,
      loader: async () => {
        const { component } = await loadWidget(widget)
        return component
      },
    }
  })

  logger.widgets("Retrieved plugin widgets", {
    pluginFrontendPath,
    widgetNames: Object.keys(result),
  })

  return result
}

export const loadWidgetComponent = async (
  pluginKey: string,
  widgetName: string,
): Promise<Component> => {
  const plugin = registeredPlugins[pluginKey]
  if (!plugin) {
    logger.widgets(`Plugin not found: ${pluginKey}`)
    throw new Error(`Plugin not found: ${pluginKey}`)
  }

  const widget = plugin.widgets?.[widgetName]
  if (!widget) {
    logger.widgets(`Widget not found: ${pluginKey}/${widgetName}`)
    throw new Error(`Widget '${widgetName}' not found in plugin '${pluginKey}'`)
  }

  try {
    const component = await widget.loader()
    if (!component) {
      throw new Error(
        `Widget loader returned null/undefined for ${pluginKey}/${widgetName}`,
      )
    }
    return component
  } catch (error) {
    const enhancedError = createEnhancedLoadError(error, pluginKey, widgetName)
    logger.widgets("Widget component load failed", {
      pluginKey,
      widgetName,
      error: enhancedError.message,
    })
    throw enhancedError
  }
}

const createEnhancedLoadError = (
  error: unknown,
  pluginKey: string,
  widgetName: string,
) => {
  if (error instanceof Error) {
    const enhancedError = new Error(
      `Failed to load widget component ${pluginKey}/${widgetName}: ${error.message}`,
    )
    enhancedError.stack = error.stack
    return enhancedError
  }
  return new Error(
    `Failed to load widget component ${pluginKey}/${widgetName}: ${String(error)}`,
  )
}

// Initialize on module load
initializeWidgetDiscovery()
