<script lang="ts">
  // The desktop shell: full-viewport surface (background + icons + the
  // centered task launcher + the floating window layer) with the taskbar
  // docked below it as a real flex sibling, not an overlay — so a maximized
  // or dragged window can never end up underneath the taskbar. This replaces
  // the old sidebar + hash-routed page shell in App.svelte entirely; apps are
  // desktop icons now (see $lib/apps.ts), not nav items.
  import { apps } from '$lib/apps'
  import { iconPositions, resetIconLayout } from '$lib/stores/icons'
  import { wm, openAppWindow, toggleShowDesktop } from '$lib/stores/windows'
  import { summary } from '$lib/stores/context'
  import { getBackground } from '$lib/stores/background.svelte'
  import { patternCss } from '$lib/desktop-patterns'
  import DesktopIcon from './DesktopIcon.svelte'
  import TaskLauncher from './TaskLauncher.svelte'
  import WindowLayer from './WindowLayer.svelte'
  import DockedLayer from './DockedLayer.svelte'
  import Taskbar from './Taskbar.svelte'
  import * as ContextMenu from '$lib/components/ui/context-menu'
  import LayersIcon from '@lucide/svelte/icons/layers'
  import Rows3Icon from '@lucide/svelte/icons/rows-3'
  import MonitorIcon from '@lucide/svelte/icons/monitor'
  import RotateCcwIcon from '@lucide/svelte/icons/rotate-ccw'
  import Undo2Icon from '@lucide/svelte/icons/undo-2'
  import Redo2Icon from '@lucide/svelte/icons/redo-2'

  // canUndo/canRedo are plain wmkit method calls (not stores), so they're
  // snapshotted once when the menu opens (onOpenChange) rather than read
  // reactively in the template. bits-ui auto-dismisses on item select and
  // on Escape / click-away, so the old manual menuPos/closeMenu/runMenuAction
  // machinery is gone.
  let menuCanUndo = $state(false)
  let menuCanRedo = $state(false)

  function onOpenChange(open: boolean) {
    if (!open) return
    menuCanUndo = wm.canUndo()
    menuCanRedo = wm.canRedo()
  }

  // Cmd/Ctrl+Z / Shift+Z for window-arrangement undo/redo (move, resize,
  // close, ...) — wmkit tracks this history but ships no default keybinding.
  // Skipped entirely while an editable element has focus so it never
  // fights the browser's own text-undo inside the task input or a form
  // field.
  function onWindowKeydown(e: KeyboardEvent) {
    const target = e.target as HTMLElement | null
    const editable =
      !!target &&
      (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)
    if (editable) return
    if (!(e.metaKey || e.ctrlKey) || e.key.toLowerCase() !== 'z') return
    e.preventDefault()
    if (e.shiftKey) wm.redo()
    else wm.undo()
  }

  // Configurable in Settings → Appearance (see background.svelte.ts). Two
  // layers, not one, because rotation and the fade mask need different
  // geometry:
  //  - outer: exactly the viewport box. Carries the fade mask, since a
  //    vignette has to be centered on what's actually visible.
  //  - inner: oversized (200%) and centered before rotating, so turning the
  //    pattern doesn't pull its straight edges into view at the corners —
  //    a viewport-sized box rotated in place would do exactly that.
  const bgActive = $derived.by(() => {
    const bg = getBackground()
    return bg.pattern !== 'none' || bg.fillColor !== null
  })
  const bgOuterStyle = $derived.by(() => {
    const bg = getBackground()
    if (bg.fade <= 0) return ''
    const stop = Math.round(100 - bg.fade * 70)
    const mask = `radial-gradient(circle at 50% 50%, black 0%, black ${stop}%, transparent 100%)`
    return `mask-image:${mask};-webkit-mask-image:${mask};`
  })
  const bgInnerStyle = $derived.by(() => {
    const bg = getBackground()
    const css = patternCss(bg.pattern, bg.color, bg.scale)
    return `inset:-50%;width:200%;height:200%;opacity:${bg.opacity};background-color:${bg.fillColor ?? 'transparent'};transform:rotate(${bg.rotation}deg);${css}`
  })
</script>

<svelte:window onkeydown={onWindowKeydown} />

<div class="fixed inset-0 flex flex-col">
  <div class="relative min-h-0 flex-1 overflow-hidden" role="presentation">
    {#if bgActive}
      <div
        class="pointer-events-none absolute inset-0 z-0 overflow-hidden"
        aria-hidden="true"
        style={bgOuterStyle}
      >
        <div class="absolute" style={bgInnerStyle}></div>
      </div>
    {/if}

    <ContextMenu.Root {onOpenChange}>
      <!-- The bare-desktop hit area. Placed before the icons/windows layers
           so they (pointer-events-auto, later in DOM → paint on top) catch
           their own right-clicks — the trigger only sees right-clicks that
           fall through to bare desktop. This DOM-structure gate replaces the
           old `currentTarget === target` event check. Left-click on bare
           desktop blurs the focused window (the familiar "click empty
           desktop to deselect" affordance). -->
      <ContextMenu.Trigger class="absolute inset-0 z-0" onclick={() => wm.blur()}
      ></ContextMenu.Trigger>
      <ContextMenu.Content class="min-w-48">
        <ContextMenu.Item onSelect={() => wm.arrange('cascade')}>
          <LayersIcon class="size-4" /> Cascade windows
        </ContextMenu.Item>
        <ContextMenu.Item onSelect={() => wm.arrange('tile')}>
          <Rows3Icon class="size-4" /> Tile windows
        </ContextMenu.Item>
        <ContextMenu.Item onSelect={toggleShowDesktop}>
          <MonitorIcon class="size-4" /> Show desktop
        </ContextMenu.Item>
        <ContextMenu.Separator />
        <ContextMenu.Item onSelect={resetIconLayout}>
          <RotateCcwIcon class="size-4" /> Reset icon layout
        </ContextMenu.Item>
        <ContextMenu.Separator />
        <ContextMenu.Item disabled={!menuCanUndo} onSelect={() => wm.undo()}>
          <Undo2Icon class="size-4" /> Undo
        </ContextMenu.Item>
        <ContextMenu.Item disabled={!menuCanRedo} onSelect={() => wm.redo()}>
          <Redo2Icon class="size-4" /> Redo
        </ContextMenu.Item>
      </ContextMenu.Content>
    </ContextMenu.Root>

    <div class="pointer-events-none absolute inset-0 z-0">
      {#each $apps as app (app.id)}
        {@const pos = $iconPositions[app.id] ?? { col: 0, row: 0 }}
        {@const badge = app.badge?.($summary) ?? 0}
        <DesktopIcon {app} {pos} {badge} onOpen={() => openAppWindow(app.id)} />
      {/each}
    </div>

    <div class="pointer-events-none absolute inset-0 z-10 flex items-center justify-center p-6">
      <div class="pointer-events-auto">
        <TaskLauncher />
      </div>
    </div>

    <WindowLayer />

    <DockedLayer />
  </div>

  <Taskbar />
</div>
