import { Suspense, useEffect, useState } from 'react'
import { Link, Outlet, useMatchRoute, useNavigate, useRouterState } from '@tanstack/react-router'
import { Toaster } from '@/components/ui/toast'
import { useLogout, useMe } from '@/lib/auth'
import { cn } from '@/lib/cn'

// Top-level sections. `icon` is only used by the mobile bottom bar; the desktop
// top bar shows labels alone. Settings is filtered to admins at render.
const sections = [
  { to: '/gardens', label: 'Gardens', icon: '🏡', adminOnly: false },
  { to: '/plants', label: 'Plants', icon: '🌿', adminOnly: false },
  { to: '/settings', label: 'Settings', icon: '⚙️', adminOnly: true },
] as const

type Section = (typeof sections)[number]

// TanStack Router concatenates the base className with activeProps/inactiveProps,
// so state-specific and conflicting utilities (text-muted vs text-fg) live in the
// state props — never in the base — to avoid ambiguous overrides.
const navLinkBase = 'rounded-md px-3 py-1.5 text-sm font-medium transition-colors'
const navLinkActive = 'bg-border/60 text-fg'
const navLinkInactive = 'text-muted hover:bg-border/60 hover:text-fg'

/**
 * Top-level chrome. Mobile-first: a slim top bar (brand + account) with the
 * section nav moved to a thumb-reachable bottom tab bar; the desktop breakpoint
 * (`md:`) restores the inline top nav. The editor is a full-screen context, so it
 * owns the bottom of the screen — the app bottom bar hides there (the brand link
 * is the way back to the gardens list), leaving exactly one bottom bar per route.
 */
export function AppShell() {
  const me = useMe()
  const user = me.data
  const matchRoute = useMatchRoute()

  // Full-screen canvas contexts own the bottom of the screen: the garden editor
  // and the public shared-garden view both render a 100dvh-8rem canvas, so a
  // signed-in viewer must not get the app bottom bar overlapping it. Fuzzy:false
  // so the '/gardens' list itself still shows the bar.
  const inEditor = !!matchRoute({ to: '/gardens/$gardenId', fuzzy: false })
  const inPublicGarden = !!matchRoute({ to: '/g/$token', fuzzy: false })
  // A canvas route wants the whole width — the garden editor is squeezed by the
  // max-w-5xl reading measure the other pages use (#107).
  const canvasRoute = inEditor || inPublicGarden
  const showBottomNav = !!user && !canvasRoute

  const visibleSections = sections.filter((s) => !s.adminOnly || user?.isAdmin)

  return (
    <div className="flex min-h-full flex-col">
      <header className="sticky top-0 z-20 border-b border-border bg-surface/90 backdrop-blur">
        {/* The bar matches the content width below: constrained on reading pages,
            edge-to-edge on the canvas routes so the brand aligns with the editor. */}
        <nav className={cn('flex items-center gap-4 px-4 py-3', canvasRoute ? '' : 'mx-auto max-w-5xl')}>
          <Link to="/gardens" className="text-lg font-semibold text-accent-strong">
            🌱 pansy
          </Link>

          {/* Desktop inline section links. Hidden on mobile, where the bottom bar
              carries them. */}
          <div className="hidden flex-1 items-center gap-1 md:flex">
            {user &&
              visibleSections.map((s) => (
                <Link
                  key={s.to}
                  to={s.to}
                  className={navLinkBase}
                  activeProps={{ className: navLinkActive }}
                  inactiveProps={{ className: navLinkInactive }}
                >
                  {s.label}
                </Link>
              ))}
          </div>

          {/* Spacer so account/sign-out sits right on mobile (the desktop links
              own flex-1 above). */}
          <div className="flex-1 md:hidden" />

          {user ? (
            <AccountMenu displayName={user.displayName} />
          ) : (
            <Link
              to="/login"
              className="rounded-md px-3 py-1.5 text-sm font-medium text-muted transition-colors hover:text-fg"
            >
              Sign in
            </Link>
          )}
        </nav>
      </header>

      <main
        className={cn(
          'w-full flex-1 px-4 py-6',
          // Constrain the reading pages to a comfortable measure; the canvas
          // routes go edge-to-edge so the garden gets the whole screen.
          !canvasRoute && 'mx-auto max-w-5xl',
          // Clear the fixed bottom bar on mobile so content isn't hidden behind
          // it. The 3.5rem must match BottomNav's h-14 (kept adjacent below).
          showBottomNav && 'pb-[calc(3.5rem+env(safe-area-inset-bottom))] md:pb-6',
        )}
      >
        {/* Boundary for the lazily-loaded routes (see router.tsx). */}
        <Suspense fallback={<p className="p-6 text-sm text-muted">Loading…</p>}>
          <Outlet />
        </Suspense>
      </main>

      {showBottomNav && <BottomNav sections={visibleSections} />}

      <Toaster />
    </div>
  )
}

/** Account control: a compact button that toggles a small sign-out popover. On
 *  desktop the display name shows inline; on mobile it lives inside the popover. */
function AccountMenu({ displayName }: { displayName: string }) {
  const logout = useLogout()
  const navigate = useNavigate()
  const [open, setOpen] = useState(false)
  const pathname = useRouterState({ select: (s) => s.location.pathname })

  // Close on any route change, so navigating (bottom nav, browser back) can't
  // leave the popover — and its full-screen backdrop — stuck open over the page.
  useEffect(() => setOpen(false), [pathname])

  async function onLogout() {
    try {
      await logout.mutateAsync()
      await navigate({ to: '/login' })
    } catch {
      // The logout request failed, so the session is still valid server-side.
      // Keep the popover OPEN so the button (now "Retry sign out", driven by
      // logout.isError) stays on screen — closing it would hide the only retry
      // affordance and pretend nothing went wrong.
    }
  }

  const initial = displayName.trim().charAt(0).toUpperCase() || '·'

  return (
    <div className="relative">
      <button
        type="button"
        onClick={() => setOpen((v) => !v)}
        aria-haspopup="menu"
        aria-expanded={open}
        className="flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-muted transition-colors hover:text-fg"
      >
        <span className="hidden sm:inline">{displayName}</span>
        <span
          aria-hidden
          className="flex size-8 items-center justify-center rounded-full bg-border/60 text-sm font-semibold text-fg"
        >
          {initial}
        </span>
      </button>

      {open && (
        <>
          {/* Full-screen click-away backdrop; reliably closes on an outside tap
              without a document listener. */}
          <button
            type="button"
            aria-label="Close menu"
            className="fixed inset-0 z-30 cursor-default"
            onClick={() => setOpen(false)}
          />
          <div
            role="menu"
            className="absolute right-0 z-40 mt-2 w-48 rounded-lg border border-border bg-surface p-1 shadow-lg"
          >
            <p className="truncate px-3 py-2 text-xs text-muted">
              Signed in as <span className="text-fg">{displayName}</span>
            </p>
            <button
              type="button"
              role="menuitem"
              onClick={onLogout}
              disabled={logout.isPending}
              title={logout.isError ? 'Sign out failed — try again' : undefined}
              className="w-full rounded-md px-3 py-2 text-left text-sm font-medium text-muted transition-colors hover:bg-border/60 hover:text-fg disabled:opacity-60"
            >
              {logout.isPending ? 'Signing out…' : logout.isError ? 'Retry sign out' : 'Sign out'}
            </button>
          </div>
        </>
      )}
    </div>
  )
}

/** Mobile bottom tab bar for the top-level sections (thumb zone, safe-area aware). */
function BottomNav({ sections }: { sections: ReadonlyArray<Section> }) {
  return (
    <nav
      aria-label="Sections"
      className="fixed inset-x-0 bottom-0 z-20 border-t border-border bg-surface/95 pb-[env(safe-area-inset-bottom)] backdrop-blur md:hidden"
    >
      <ul className="mx-auto flex max-w-5xl items-stretch justify-around">
        {sections.map((s) => (
          <li key={s.to} className="flex-1">
            {/* h-14 matches the clearance reserved on <main> above. Color lives
                only in the state props (per the top-bar convention), never the
                base, so active/inactive don't fight. */}
            <Link
              to={s.to}
              className="flex h-14 flex-col items-center justify-center gap-0.5 text-xs font-medium transition-colors"
              activeProps={{ className: 'text-accent-strong' }}
              inactiveProps={{ className: 'text-muted hover:text-fg' }}
            >
              <span aria-hidden className="text-lg leading-none">
                {s.icon}
              </span>
              {s.label}
            </Link>
          </li>
        ))}
      </ul>
    </nav>
  )
}
