import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/auth'
import AppLayout from '../views/AppLayout.vue'
import SettingsLayout from '../views/settings/SettingsLayout.vue'
import { setupComplete, setupSkipped, isSetupLoaded, refreshSetupState } from '../composables/setupState'
import { loadEnabledPlugins, isPluginEnabled } from '../composables/enabledPlugins'
import { useToast } from '../composables/toast'
import { getFacilityName } from '../utils/siteSettings'
import { stagedChildren, stagedToplevel } from './routes.gen'

// Auto-discover all route modules from routes/ directory. A module's default
// export is AppLayout child routes; an optional `toplevel` export is full-screen
// routes (kiosk, print pages) that live OUTSIDE AppLayout. Both let a plugin own
// its routes, so pruning the plugin removes them with no core edit (ADR-013).
const routeModules = import.meta.glob('./routes/*.js', { eager: true })
// Staged plugin frontends (relocated to plugins/<name>/frontend/) are
// aggregated into routes.gen.js by scripts/stage-frontend.mjs. In-tree route
// files (./routes/*.js) cover plugins not yet relocated - dual-location during
// the Phase 4 transition.
const rawChildren = [
  // A route file may export only `toplevel` (no AppLayout children), so guard
  // the default with an empty array.
  ...Object.values(routeModules).flatMap(m => m.default || []),
  ...stagedChildren,
]
const pluginTopLevel = [
  ...Object.values(routeModules).flatMap(m => m.toplevel || []),
  ...stagedToplevel,
]

// Gather the settings pages (spread across plugin route files) and nest them
// under a single two-pane shell so the grouped rail stays put while the right
// pane swaps. Slides is a sidebar page that happens to live at /settings/slides;
// keep it full-width (not inside the settings rail).
const SETTINGS_STANDALONE = new Set(['settings/slides'])
const settingsChildren = []
const otherChildren = []
for (const route of rawChildren) {
  const path = route.path
  if (path === 'settings') {
    // Old index becomes the shell's default child (keeps name 'settings').
    settingsChildren.unshift({ ...route, path: '' })
  } else if (typeof path === 'string' && path.startsWith('settings/') && !SETTINGS_STANDALONE.has(path)) {
    settingsChildren.push({ ...route, path: path.replace(/^settings\//, '') })
  } else {
    otherChildren.push(route)
  }
}
const appChildren = [
  ...otherChildren,
  {
    path: 'settings',
    component: SettingsLayout,
    meta: { requiresAuth: true, requiresAdmin: true },
    children: settingsChildren,
  },
]

const routes = [
  {
    path: '/login',
    name: 'login',
    component: () => import('../views/Login.vue'),
    meta: { guest: true }
  },
  // Forced/self-service password change (standalone, authenticated)
  {
    path: '/change-password',
    name: 'change-password',
    component: () => import('../views/ChangePassword.vue'),
    meta: { requiresAuth: true }
  },
  // First-run setup wizard (standalone, admin-only)
  {
    path: '/setup',
    name: 'setup',
    component: () => import('../views/SetupWizard.vue'),
    meta: { requiresAuth: true, requiresAdmin: true }
  },
  // Standalone full-screen dashboards (no sidebar, no auth required)
  {
    path: '/shopfloor',
    name: 'shopfloor',
    component: () => import('../views/ShopfloorDashboard.vue')
  },
  // Plugin-owned full-screen routes (kiosk, TV, plugin print pages) are
  // contributed by each plugin's route file via its `toplevel` export.
  ...pluginTopLevel,
  // Core print pages (standalone, no sidebar/header) - span multiple asset
  // types or are core-owned, so they stay here.
  {
    path: '/print/machine-badge/:id',
    name: 'print-machine-badge',
    component: () => import('../views/print/MachineBadge.vue')
  },
  // Shared asset label/code generator for any asset type (public, like the
  // other /print/* routes). assettype = machine|computer|printer|
  // network_device|measuring_tool; id = the asset's plugin id.
  {
    path: '/print/asset-label/:assettype/:id',
    name: 'print-asset-label',
    component: () => import('../views/print/AssetLabel.vue')
  },
  // Batch label sheets (ULINE 6-up / mini 72-up) for one asset type.
  {
    path: '/print/asset-label-batch/:assettype',
    name: 'print-asset-label-batch',
    component: () => import('../views/print/AssetLabelBatch.vue')
  },
  {
    path: '/',
    component: AppLayout,
    children: appChildren
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

// --- Document titles: "<Facility> ShopDB - <Page>" ---------------------------
// Facility name comes from public settings (cached after first fetch); the
// page label comes from meta.title when a route sets one, else a prettified
// route name with spellings for the odd ones.
const TITLE_SPELLINGS = {
  'pcs': 'PCs',
  'usb': 'USB Devices',
  'geenforce': 'GE-Enforce',
  'knowledgebase': 'Knowledge Base',
  'printedparts': '3D Printed Parts',
  'parts-kiosk': 'Parts Kiosk',
  'tv': 'TV Slideshow',
  'shopfloor': 'Shopfloor Dashboard',
  'measuringtools': 'Measuring Tools',
  'networkdevices': 'Network',
}

function pageTitleFor(route) {
  if (route.meta?.title) return route.meta.title
  const name = String(route.name || '')
  if (!name) return ''
  const base = name.replace(/-(new|edit|detail)$/, '')
  if (TITLE_SPELLINGS[base]) return TITLE_SPELLINGS[base]
  return base.split('-').map(word =>
    word.charAt(0).toUpperCase() + word.slice(1)).join(' ')
}

router.afterEach(async (to) => {
  let siteTitle = 'ShopDB'
  try {
    const facility = await getFacilityName()
    if (facility && facility !== 'ShopDB') siteTitle = `${facility} ShopDB`
  } catch (titleError) { /* settings unavailable - plain ShopDB */ }
  const page = pageTitleFor(to)
  document.title = page && page !== 'Home' ? `${siteTitle} - ${page}` : siteTitle
})

// Navigation guard
router.beforeEach(async (to, from, next) => {
  const authStore = useAuthStore()

  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    // Remember where they were headed so login can send them back there.
    return next({ path: '/login', query: { redirect: to.fullPath } })
  }
  if (to.meta.requiresAdmin && !authStore.isAdmin) {
    return next('/')
  }
  if (to.meta.guest && authStore.isAuthenticated) {
    return next('/')
  }

  // Forced password change: an admin-set temporary password must be replaced
  // before the user reaches the rest of the app. Let them log out.
  if (authStore.isAuthenticated && authStore.mustChangePassword
      && to.path !== '/change-password' && to.path !== '/login') {
    return next('/change-password')
  }

  // Plugin gating: a disabled backend plugin's frontend routes are dead ends.
  // The enabled list is fetched once and cached; fail-open on error so a blip
  // cannot brick navigation. Works unauthenticated (endpoint is jwt-optional).
  if (to.meta.plugin) {
    await loadEnabledPlugins()
    if (!isPluginEnabled(to.meta.plugin)) {
      useToast().info(`The ${to.meta.plugin} feature is not enabled.`)
      return next('/')
    }
  }

  // First-run: steer a fresh admin into the setup wizard (once, until finished).
  if (authStore.isAuthenticated && authStore.isAdmin && to.path !== '/setup') {
    if (!isSetupLoaded()) {
      await refreshSetupState()
    }
    if (!setupComplete.value && !setupSkipped.value) {
      return next('/setup')
    }
  }

  next()
})

export default router
