import type { ReactNode } from 'react';
import {
  Boxes,
  Palette,
  CalendarClock,
  CalendarDays,
  CalendarRange,
  Clapperboard,
  Film,
  FolderTree,
  KeyRound,
  Layers,
  LayoutDashboard,
  LayoutGrid,
  Library,
  Link2,
  ListChecks,
  ListMusic,
  ListVideo,
  Plus,
  Repeat,
  ScrollText,
  Search,
  Settings,
  SlidersHorizontal,
  Sparkles,
  Stamp,
  Stethoscope,
  Trash2,
  Tv
} from 'lucide-react';

export type ScreenId =
  | 'dashboard'
  | 'channels'
  | 'builder'
  | 'autoTune'
  | 'editChannel'
  | 'guide'
  | 'schedules'
  | 'blocks'
  | 'templates'
  | 'decos'
  | 'decoTemplates'
  | 'playouts'
  | 'media'
  | 'search'
  | 'trash'
  | 'collections'
  | 'multiCollections'
  | 'rerunCollections'
  | 'playlists'
  | 'fillerPresets'
  | 'libraries'
  | 'traktLists'
  | 'ffmpegProfiles'
  | 'watermarks'
  | 'settings'
  | 'apiKey'
  | 'logs'
  | 'troubleshooting'
  | 'blockPlayoutTroubleshooting'
  | 'playbackTroubleshooting'
  | 'yamlValidator';

export interface ScreenRoute {
  id: ScreenId;
  path: string;
  label: string;
  title: string;
  kicker: string;
  description: string;
  icon: ReactNode;
  primaryAction: string;
  placeholder: string;
  badge?: number;
  // Screens that own linkable sub-sections (e.g. /app/settings/streaming) opt in here;
  // the sub-path is otherwise unowned and 404s (see routeFromLocation).
  allowSubPaths?: boolean;
}

// This is the one stable route-object table for the application. routeFromLocation returns
// references from this array so allowSubPaths navigations intentionally preserve object identity.
export const routes: ScreenRoute[] = [
  {
    id: 'dashboard',
    path: '/app',
    label: 'Dashboard',
    title: 'Dashboard',
    kicker: 'Overview',
    description: 'On-air status, health checks, and recent server activity.',
    icon: <LayoutDashboard aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Dashboard workspace'
  },
  {
    id: 'channels',
    path: '/app/channels',
    label: 'Channels',
    title: 'Channels',
    kicker: 'Lineup',
    description: 'Dense channel table, ordering tools, bulk actions, and playback status.',
    icon: <Tv aria-hidden="true" size={16} />,
    primaryAction: 'Add Channel',
    placeholder: 'Channel list workspace'
  },
  {
    id: 'builder',
    path: '/app/new-channel',
    label: 'New Channel',
    title: 'New Channel',
    kicker: 'Builder',
    description: 'Library-to-lineup channel creation flow with template-backed defaults.',
    icon: <Plus aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Channel builder workspace'
  },
  {
    // Net-new SPA screen with no Blazor ancestor (#69). The 3-step wizard drives its own in-body
    // buttons, so it registers no TopBar primaryAction (spa-conventions §10).
    id: 'autoTune',
    path: '/app/auto-tune',
    label: 'Auto-Tune',
    title: 'Auto-Tune',
    kicker: 'Builder',
    description: 'Generate a whole channel lineup from your library metadata, preview it, then bulk-create.',
    icon: <Sparkles aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Auto-tune workspace'
  },
  {
    // Not in the sidebar nav; reached via the edit pencil on the channel table. The
    // screen owns parsing the {id} suffix (see ChannelEditScreen), so it opts into
    // sub-paths like /app/edit-channel/5.
    id: 'editChannel',
    path: '/app/edit-channel',
    label: 'Edit Channel',
    title: 'Edit Channel',
    kicker: 'Channel',
    description: 'Full channel editor: identity, playout, streaming, selection and branding.',
    icon: <Tv aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Channel editor workspace',
    allowSubPaths: true
  },
  {
    id: 'guide',
    path: '/app/guide',
    label: 'Guide',
    title: 'Guide',
    kicker: 'EPG',
    description: 'Timeline grid with channel rows, programme blocks, and a live now marker.',
    icon: <LayoutGrid aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'EPG grid workspace'
  },
  {
    id: 'schedules',
    path: '/app/schedules',
    label: 'Schedules',
    title: 'Schedules',
    kicker: 'Programming',
    description: 'Two-pane schedule editor with drag ordering and progressive details.',
    icon: <CalendarClock aria-hidden="true" size={16} />,
    primaryAction: 'Add Schedule',
    placeholder: 'Schedule editor workspace'
  },
  {
    // The editor lives at a sub-path (/app/blocks/{id}); BlocksScreen owns parsing the id suffix.
    id: 'blocks',
    path: '/app/blocks',
    label: 'Blocks',
    title: 'Blocks',
    kicker: 'Programming',
    description: 'Group blocks and edit their items, durations, and playout preview.',
    icon: <Boxes aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Blocks workspace',
    allowSubPaths: true
  },
  {
    // The editor lives at a sub-path (/app/templates/{id}); TemplatesScreen owns parsing the id suffix.
    id: 'templates',
    path: '/app/templates',
    label: 'Templates',
    title: 'Templates',
    kicker: 'Programming',
    description: 'Group templates and assign blocks to times of day.',
    icon: <CalendarDays aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Templates workspace',
    allowSubPaths: true
  },
  {
    // The editor lives at a sub-path (/app/decos/{id}); DecosScreen owns parsing the id suffix.
    id: 'decos',
    path: '/app/decos',
    label: 'Decos',
    title: 'Decos',
    kicker: 'Programming',
    description: 'Group decos and configure watermark, graphics, break content, and filler overrides.',
    icon: <Palette aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Decos workspace',
    allowSubPaths: true
  },
  {
    // The editor lives at a sub-path (/app/deco-templates/{id}); DecoTemplatesScreen owns parsing the id suffix.
    id: 'decoTemplates',
    path: '/app/deco-templates',
    label: 'Deco Templates',
    title: 'Deco Templates',
    kicker: 'Programming',
    description: 'Group deco templates and assign decos to times of day.',
    icon: <CalendarRange aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Deco templates workspace',
    allowSubPaths: true
  },
  {
    // The alternate-schedules / templates editors live at sub-paths
    // (/app/playouts/{id}/alternate-schedules, /app/playouts/{id}/templates).
    id: 'playouts',
    path: '/app/playouts',
    label: 'Playouts',
    title: 'Playouts',
    kicker: 'Runtime',
    description: 'Playout state, reset controls, timeline diagnostics, and build warnings.',
    icon: <ListVideo aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Playouts workspace',
    allowSubPaths: true
  },
  {
    id: 'media',
    path: '/app/media',
    label: 'Browse',
    title: 'Browse Media',
    kicker: 'Media',
    description: 'Browse indexed movies, shows, episodes, music and more by kind.',
    icon: <Clapperboard aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Media browse workspace',
    allowSubPaths: true
  },
  {
    id: 'search',
    path: '/app/search',
    label: 'Search',
    title: 'Search',
    kicker: 'Media',
    description: 'Search across every media kind at once.',
    icon: <Search aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Search workspace',
    allowSubPaths: true
  },
  {
    id: 'trash',
    path: '/app/trash',
    label: 'Trash',
    title: 'Trash',
    kicker: 'Media',
    description: 'Library items whose files have gone missing.',
    icon: <Trash2 aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Trash workspace'
  },
  {
    id: 'collections',
    path: '/app/collections',
    label: 'Collections',
    title: 'Collections',
    kicker: 'Media',
    description: 'Manual, smart, multi, playlist, search, and rerun collection management.',
    icon: <FolderTree aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Collections workspace'
  },
  {
    id: 'multiCollections',
    path: '/app/multi-collections',
    label: 'Multi-Collections',
    title: 'Multi-Collections',
    kicker: 'Media',
    description: 'Group manual and smart collections into a single schedulable collection.',
    icon: <Layers aria-hidden="true" size={16} />,
    primaryAction: 'Add Multi-Collection',
    placeholder: 'Multi-collections workspace'
  },
  {
    id: 'rerunCollections',
    path: '/app/rerun-collections',
    label: 'Rerun Collections',
    title: 'Rerun Collections',
    kicker: 'Media',
    description: 'A collection with distinct first-run and rerun playback orders.',
    icon: <Repeat aria-hidden="true" size={16} />,
    primaryAction: 'Add Rerun Collection',
    placeholder: 'Rerun collections workspace'
  },
  {
    id: 'playlists',
    path: '/app/playlists',
    label: 'Playlists',
    title: 'Playlists',
    kicker: 'Media',
    description: 'Group, order, and preview playlist items scheduled into channels.',
    icon: <ListMusic aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Playlists workspace'
  },
  {
    id: 'fillerPresets',
    path: '/app/filler-presets',
    label: 'Filler Presets',
    title: 'Filler Presets',
    kicker: 'Media',
    description: 'Pre/mid/post-roll, tail and fallback filler presets used by schedules.',
    icon: <Film aria-hidden="true" size={16} />,
    primaryAction: 'Add Filler Preset',
    placeholder: 'Filler presets workspace',
    allowSubPaths: true
  },
  {
    // The media-source editors live at sub-paths (/app/libraries/local/{id}, /app/libraries/plex,
    // /app/libraries/{jellyfin|emby}/connection, .../{id}/sync, .../{id}/path-replacements).
    // Unlike other allowSubPaths screens, the libraries wrapper does NOT self-listen for popstate:
    // its editors register a dirty guard, so App owns pathname/popstate and passes the approved
    // sub-path down (see LibrariesRouteScreen + spa-conventions §8, design §D.2).
    id: 'libraries',
    path: '/app/libraries',
    label: 'Libraries',
    title: 'Libraries',
    kicker: 'Sources',
    description: 'Local, Plex, Jellyfin, and Emby media source monitoring.',
    icon: <Library aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Libraries workspace',
    allowSubPaths: true
  },
  {
    // The editor lives at a sub-path (/app/trakt-lists/{id}); the screen owns parsing the
    // {id} suffix itself (see TraktListsScreen), same pattern as editChannel/settings.
    id: 'traktLists',
    path: '/app/trakt-lists',
    label: 'Trakt Lists',
    title: 'Trakt Lists',
    kicker: 'Media',
    description: 'Add, match, and manage Trakt list imports.',
    icon: <Link2 aria-hidden="true" size={16} />,
    primaryAction: 'Add Trakt List',
    placeholder: 'Trakt lists workspace',
    allowSubPaths: true
  },
  {
    id: 'ffmpegProfiles',
    path: '/app/ffmpeg-profiles',
    label: 'FFmpeg Profiles',
    title: 'FFmpeg Profiles',
    kicker: 'System',
    description: 'Transcoding profiles: resolution, video/audio formats, hardware acceleration.',
    icon: <SlidersHorizontal aria-hidden="true" size={16} />,
    primaryAction: 'Add Profile',
    placeholder: 'FFmpeg profiles workspace',
    allowSubPaths: true
  },
  {
    id: 'watermarks',
    path: '/app/watermarks',
    label: 'Watermarks',
    title: 'Watermarks',
    kicker: 'System',
    description: 'Channel watermark overlays: image, position, size and opacity.',
    icon: <Stamp aria-hidden="true" size={16} />,
    primaryAction: 'Add Watermark',
    placeholder: 'Watermarks workspace',
    allowSubPaths: true
  },
  {
    id: 'settings',
    path: '/app/settings',
    label: 'Settings',
    title: 'Settings',
    kicker: 'System',
    description: 'Server configuration, access keys, FFmpeg profiles, and UI preferences.',
    icon: <Settings aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Settings workspace',
    allowSubPaths: true
  },
  {
    // Since #295 the browser authenticates by session cookie; this screen only *displays* the
    // server machine key (for MCP / external REST clients) and, for local accounts, changes the
    // admin password — there is no key to "save" here (see spa-conventions §5e).
    id: 'apiKey',
    path: '/app/api-key',
    label: 'API Key',
    title: 'API Key',
    kicker: 'System',
    description: 'View the machine key for MCP and external REST clients, and change the local admin password.',
    icon: <KeyRound aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'API key workspace'
  },
  {
    id: 'logs',
    path: '/app/logs',
    label: 'Logs',
    title: 'Logs',
    kicker: 'System',
    description: 'Recent server log entries with level and free-text filtering.',
    icon: <ScrollText aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Logs workspace'
  },
  {
    id: 'troubleshooting',
    path: '/app/troubleshooting',
    label: 'Troubleshooting',
    title: 'Troubleshooting',
    kicker: 'System',
    description: 'Version, environment, hardware and FFmpeg capability diagnostics.',
    icon: <Stethoscope aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Troubleshooting workspace'
  },
  {
    id: 'blockPlayoutTroubleshooting',
    path: '/app/troubleshooting/blocks',
    label: 'Block History',
    title: 'Block Playout Troubleshooting',
    kicker: 'System',
    description: 'Inspect a block playout, its blocks, and each block’s scheduling history.',
    icon: <Boxes aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Block playout troubleshooting workspace'
  },
  {
    id: 'playbackTroubleshooting',
    path: '/app/troubleshooting/playback',
    label: 'Playback Troubleshooting',
    title: 'Playback Troubleshooting',
    kicker: 'System',
    description: 'Preview channel or media-item playback with a chosen FFmpeg profile and inspect the transcode logs.',
    icon: <Stethoscope aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Playback troubleshooting workspace'
  },
  {
    id: 'yamlValidator',
    path: '/app/troubleshooting/yaml',
    label: 'Schedule Validator',
    title: 'Sequential Schedule Validator',
    kicker: 'System',
    description: 'Validate a sequential-schedule YAML document against the schema.',
    icon: <ListChecks aria-hidden="true" size={16} />,
    primaryAction: '',
    placeholder: 'Sequential schedule validator workspace'
  }
];

export const routeById = new Map(routes.map((route) => [route.id, route]));

export interface SidebarNavGroupDefinition {
  label?: string;
  // Stable key for persisted collapse state (ctv-sidebar-groups). Only labeled (collapsible)
  // groups need one; the unlabeled Primary group is always open (#396).
  key?: string;
  ids: readonly ScreenId[];
}

export const sidebarNavGroups: SidebarNavGroupDefinition[] = [
  {
    ids: [
      'dashboard',
      'channels',
      'builder',
      'autoTune',
      'guide',
      'schedules',
      'blocks',
      'templates',
      'decos',
      'decoTemplates',
      'playouts'
    ]
  },
  {
    label: 'Media',
    key: 'media',
    ids: [
      'media',
      'search',
      'trash',
      'collections',
      'multiCollections',
      'rerunCollections',
      'playlists',
      'fillerPresets',
      'libraries',
      'traktLists'
    ]
  },
  {
    label: 'System',
    key: 'system',
    ids: [
      'settings',
      'apiKey',
      'logs',
      'troubleshooting',
      'blockPlayoutTroubleshooting',
      'yamlValidator',
      'ffmpegProfiles',
      'watermarks'
    ]
  }
];

export function normalizeAppPath(pathname: string): string {
  return pathname.replace(/\/+$/, '') || '/app';
}

export function routeFromLocation(): ScreenRoute | null {
  const pathname = normalizeAppPath(window.location.pathname);
  const exactMatch = routes.find((route) => route.path === pathname);

  if (exactMatch) {
    return exactMatch;
  }

  // Fall back to a sub-path of a route that has explicitly opted into linkable
  // sub-sections (e.g. /app/settings/streaming) - the screen owns parsing the
  // suffix itself (see SettingsScreen).
  const prefixMatches = routes
    .filter((route) => route.allowSubPaths && pathname.startsWith(`${route.path}/`))
    .sort((a, b) => b.path.length - a.path.length);

  return prefixMatches[0] ?? null;
}

export function routeHref(route: ScreenRoute): string {
  return route.id === 'dashboard' ? '/app' : route.path;
}
