/**
 * @uniweb/runtime - Main Entry Point
 *
 * Minimal runtime for loading foundations and orchestrating rendering.
 * Foundations should import components from @uniweb/kit.
 */

import React from 'react'
import { createRoot } from 'react-dom/client'

import { initUniweb, decodeData } from './setup.js'
import { wireTracker } from './wire-foundation.js'
import { loadFoundation, loadExtensions } from './foundation-loader.js'
import { initAppearance } from './appearance.js'
import RuntimeProvider from './RuntimeProvider.jsx'

/**
 * Get the router basename from Vite's BASE_URL
 * Vite sets this based on the `base` config option
 * Returns undefined for root path ('/'), otherwise strips trailing slash
 */
function getBasename() {
  const baseUrl = import.meta.env?.BASE_URL
  if (!baseUrl || baseUrl === '/') return undefined
  // Remove trailing slash for BrowserRouter compatibility
  return baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl
}

/**
 * Initialize the Runtime Environment
 *
 * @param {string|Object|Promise} foundationSource - One of:
 *   - URL string to foundation module
 *   - Object with {url, cssUrl}
 *   - Promise that resolves to a foundation module (legacy federation support)
 * @param {Object} options
 * @param {boolean} options.development - Enable development mode
 * @param {Object} options.configData - Site configuration (or read from DOM)
 * @param {string} options.basename - Router basename
 */
async function initRuntime(foundationSource, options = {}) {
  const {
    development = import.meta.env?.DEV ?? false,
    configData: providedConfig = null,
    basename
  } = options

  // Get config data from options, DOM, or global
  const configData =
    providedConfig ??
    JSON.parse(document.getElementById('__SITE_CONTENT__')?.textContent || 'null') ??
    globalThis.__SITE_CONTENT__

  if (!configData) {
    console.error('[Runtime] No site configuration found')
    return
  }

  try {
    let foundation

    // Handle different foundation source types
    if (typeof foundationSource === 'string' || (foundationSource && typeof foundationSource.url === 'string')) {
      // ESM URL - load via dynamic import
      foundation = await loadFoundation(foundationSource)
    } else if (foundationSource && typeof foundationSource.then === 'function') {
      // Promise (legacy Module Federation support)
      const remoteModule = await foundationSource
      // Handle double default wrapping
      foundation = remoteModule?.default?.default ? remoteModule.default : remoteModule
    } else if (foundationSource && typeof foundationSource === 'object') {
      // Already a foundation module
      foundation = foundationSource
    }

    if (!foundation) {
      throw new Error('Failed to load foundation')
    }

    // Load extensions (secondary foundations) in parallel with foundation
    // processing. An extension that fails to load doesn't block the site.
    const extensionSources = configData?.config?.extensions || []
    const extensions = extensionSources.length > 0
      ? await loadExtensions(extensionSources)
      : []

    // Build the Uniweb singleton with foundation + extensions wired in upfront
    // (dispatcher is assembled inside the Website constructor).
    const uniwebInstance = initUniweb({ content: configData, foundation, extensions })

    // Derive basename
    const routerBasename = basename ?? getBasename()

    // L2: give the tracker its destination, if the site or its host declares
    // one. Must come AFTER the basename is derived — a root-relative endpoint
    // is joined to it, and `website.basePath` is not filled until
    // RuntimeProvider renders. No destination declared (the default) leaves the
    // disabled tracker in place and costs nothing.
    //
    // `loadScripts` is passed in rather than imported by `wire-foundation.js`,
    // which is bundled for SSR/Workers where there is no DOM. This is the
    // browser entry, so this is where the DOM half belongs.
    //
    // ⭐ **Loaded on demand, and that is the point.** The import sits inside
    // the callback, so the script loader becomes its own chunk and **a site that
    // declares none never downloads it** — which matters because the runtime
    // and core are not tree-shaken and reach every site regardless. The extra
    // request lands only on sites that do declare one, after hydration, and
    // those are about to fetch a vendor script from another origin anyway.
    wireTracker(uniwebInstance, {
      basePath: routerBasename || '',
      loadScripts: (declaredScripts, opts) =>
        import('./script-loader.js')
          .then((m) => m.loadScripts(declaredScripts, opts))
          // Same contract as a script that 404s: nothing surfaces, because nobody
          // downstream could act on it. `debug` is the operator's switch.
          .catch((err) => {
            if (opts?.debug) {
              console.warn('[uniweb] tracking script loader unavailable:', err?.message || err)
            }
          })
    })

    // Apply the visitor's color scheme before React renders. Must stay ahead of
    // createRoot().render() below — see appearance.js for why this is the only
    // place the boot scheme is resolved.
    initAppearance(uniwebInstance.activeWebsite?.themeData?.appearance)

    // Set initial active page from browser URL so getLocaleUrl() works on first render
    const website = uniwebInstance.activeWebsite
    if (website && typeof window !== 'undefined') {
      const rawPath = window.location.pathname
      const basePath = routerBasename || ''
      const routePath = basePath && rawPath.startsWith(basePath)
        ? rawPath.slice(basePath.length) || '/'
        : rawPath
      website.setActivePage(routePath)
    }

    // Render the app
    const container = document.getElementById('root')
    if (!container) {
      console.error('[Runtime] Root element not found')
      return
    }

    const app = <RuntimeProvider basename={routerBasename} development={development} />

    createRoot(container).render(app)

    // Log success
    if (!development) {
      console.log(
        '%c<%c>%c Uniweb Runtime',
        'color: #FA8400; font-weight: bold; font-size: 18px;',
        'color: #00ADFE; font-weight: bold; font-size: 18px;',
        'color: #333; font-size: 18px; font-family: system-ui, sans-serif;'
      )
    }
  } catch (error) {
    console.error('[Runtime] Initialization failed:', error)

    // Render error state
    const container = document.getElementById('root')
    if (container) {
      const root = createRoot(container)
      root.render(
        <div
          style={{
            padding: '2rem',
            margin: '1rem',
            background: '#fef2f2',
            borderRadius: '0.5rem',
            color: '#dc2626'
          }}
        >
          <h2>Runtime Error</h2>
          <p>{error.message}</p>
          {development && <pre style={{ fontSize: '0.75rem', overflow: 'auto' }}>{error.stack}</pre>}
        </div>
      )
    }
  }
}

/**
 * Simplified entry point for sites
 *
 * Template main.js calls this with config + foundation/styles promises:
 *
 *   start({
 *     config: __FOUNDATION_CONFIG__,
 *     styles: import('#foundation/styles'),
 *     foundation: import('#foundation')
 *   })
 *
 * In runtime mode, the foundation/styles promises resolve to noop modules
 * (configured by the site Vite plugin) and are ignored.
 *
 * @param {Object} options
 * @param {Object} options.config - Build-time config from __FOUNDATION_CONFIG__
 * @param {Promise} options.foundation - Promise from import('#foundation')
 * @param {Promise} options.styles - Promise from import('#foundation/styles')
 */
async function start({ config, foundation, styles } = {}) {
  // Try __DATA__ first (dynamic backends inject combined config + content)
  const data = await decodeData()

  if (data) {
    // Dynamic backend mode - foundation loaded from URL, content from data
    // The serving layer may inject config.base for subdirectory deployments
    const base = data.content?.config?.base
    const basename = base ? (base.endsWith('/') ? base.slice(0, -1) : base) : undefined
    return initRuntime(
      { url: data.foundation.url, cssUrl: data.foundation.cssUrl },
      { configData: data.content, basename }
    )
  }

  // Use provided config, or fall back to bundled mode
  const mode = config?.mode ?? 'bundled'

  if (mode === 'runtime') {
    // Runtime mode - foundation loaded from URL
    return initRuntime({ url: config.url, cssUrl: config.cssUrl })
  } else {
    // Bundled mode - use the provided foundation/styles promises
    const [foundationModule] = await Promise.all([foundation, styles])
    return initRuntime(foundationModule)
  }
}

export { initRuntime, start }
export default initRuntime
