import { useState, useEffect, lazy, Suspense } from 'react'
import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
import { AnimatePresence, motion } from 'framer-motion'
import { jellyfinClient } from './api/jellyfin'
import type { AuthState } from './api/types'
import { usePreferencesStore } from './stores/preferences-store'
import { isTauri } from './lib/tauri'
import ErrorBoundary from './components/ui/ErrorBoundary'
import AppShell from './components/layout/AppShell'
import Titlebar from './components/layout/Titlebar'
import LoginPage from './pages/LoginPage'
import HomePage from './pages/HomePage'
import { useNewReleaseNotifications } from './hooks/use-new-releases'

const LibraryPage = lazy(() => import('./pages/LibraryPage'))
const DetailPage = lazy(() => import('./pages/DetailPage'))
const MusicPage = lazy(() => import('./pages/MusicPage'))
const SearchPage = lazy(() => import('./pages/SearchPage'))
const DiscoverPage = lazy(() => import('./pages/DiscoverPage'))
const SettingsPage = lazy(() => import('./pages/SettingsPage'))
const PlayerPage = lazy(() => import('./pages/PlayerPage'))
const PersonPage = lazy(() => import('./pages/PersonPage'))
const CollectionPage = lazy(() => import('./pages/CollectionPage'))
const PlaylistsPage = lazy(() => import('./pages/PlaylistsPage'))
const ProfilePage = lazy(() => import('./pages/ProfilePage'))
const StatsPage = lazy(() => import('./pages/StatsPage'))
const DuplicatesPage = lazy(() => import('./pages/DuplicatesPage'))
const DownloadsPage = lazy(() => import('./pages/DownloadsPage'))
const LiveTvPage = lazy(() => import('./pages/LiveTvPage'))
const RequestsPage = lazy(() => import('./pages/RequestsPage'))

const pageVariants = {
  initial: { opacity: 0, y: 8 },
  animate: { opacity: 1, y: 0 },
  exit: { opacity: 0, y: -4 },
}

const pageTransition = {
  duration: 0.2,
  ease: [0, 0, 0.2, 1] as [number, number, number, number],
}

/**
 * Wraps the entire window so every state of the app (loading, login, main
 * shell) shares the same titlebar + content layout. The titlebar is only
 * mounted when running inside Tauri - in a browser, the wrapper just shrinks
 * to nothing and the content fills the viewport like before.
 */
function WindowFrame({ children }: { children: React.ReactNode }) {
  const [fullscreen, setFullscreen] = useState(false)

  useEffect(() => {
    const sync = () => setFullscreen(Boolean(document.fullscreenElement))
    sync()
    document.addEventListener('fullscreenchange', sync)
    return () => document.removeEventListener('fullscreenchange', sync)
  }, [])

  return (
    <div className="h-screen flex flex-col bg-void">
      {isTauri && !fullscreen && <Titlebar />}
      <div className="flex-1 relative overflow-hidden">{children}</div>
    </div>
  )
}

function PageMotion({ children }: { children: React.ReactNode }) {
  return (
    <motion.div
      variants={pageVariants}
      initial="initial"
      animate="animate"
      exit="exit"
      transition={pageTransition}
      className="min-h-full"
    >
      {children}
    </motion.div>
  )
}

function PageFallback() {
  return (
    <div className="h-full grid place-items-center">
      <div className="flex items-center gap-2 text-text-3 text-[12.5px] font-medium">
        <span className="w-1 h-1 rounded-full bg-accent animate-pulse" />
        Loading
      </div>
    </div>
  )
}

/**
 * Renders the landing page chosen in Settings ("home" / "movies" / "shows").
 * Reads the pref synchronously so initial mount doesn't flicker through Home.
 */
function LandingRedirect() {
  const landing = usePreferencesStore(s => s.defaultLanding)
  if (landing === 'movies') return <Navigate to="/movies" replace />
  if (landing === 'shows') return <Navigate to="/shows" replace />
  return <HomePage />
}

export default function App() {
  const [auth, setAuth] = useState<AuthState | null | undefined>(undefined)
  const [loading, setLoading] = useState(true)
  const location = useLocation()
  const uiZoom = usePreferencesStore(s => s.uiZoom)
  const pushNotifications = usePreferencesStore(s => s.pushNotifications)
  useNewReleaseNotifications(pushNotifications)

  useEffect(() => {
    jellyfinClient.tryAutoLogin().then(result => {
      setAuth(result)
      setLoading(false)
    })
  }, [])

  // Apply the user's interface-zoom preference. In Tauri we use the native
  // webview zoom API (Chromium's setZoom), which is the same path Ctrl+/Ctrl-
  // takes - it reflows layout and re-evaluates media queries, so a 1.5x
  // zoom collapses multi-column grids exactly the way a smaller window would.
  // CSS `zoom` only scales rendered pixels (no reflow) so it's only used as
  // the browser fallback where the OS-level webview API isn't available.
  useEffect(() => {
    const z = Number.isFinite(uiZoom) && uiZoom > 0 ? uiZoom : 1
    if (isTauri) {
      // Clear any leftover CSS zoom from a previous session
      document.documentElement.style.zoom = ''
      // Lazy-import so the @tauri-apps/api dep doesn't get pulled into a
      // pure-browser build that doesn't need it.
      import('@tauri-apps/api/webview').then(({ getCurrentWebview }) => {
        getCurrentWebview().setZoom(z).catch(() => {})
      }).catch(() => {})
    } else {
      document.documentElement.style.zoom = String(z)
    }
  }, [uiZoom])

  // Apply custom accent color to CSS variables so the entire UI picks it up.
  const accentColor = usePreferencesStore(s => s.accentColor)
  useEffect(() => {
    const root = document.documentElement
    if (!accentColor || accentColor === '#F5B642') {
      root.style.removeProperty('--color-accent')
      root.style.removeProperty('--color-accent-hover')
      root.style.removeProperty('--color-accent-press')
      root.style.removeProperty('--color-accent-dim')
      root.style.removeProperty('--color-accent-glow')
      root.style.removeProperty('--color-accent-text')
      root.style.removeProperty('--color-accent-deep')
    } else {
      root.style.setProperty('--color-accent', accentColor)
      // Derive related shades from the base color. We use simple HSL
      // shifts so the palette stays coherent without pulling in a color
      // math library.
      try {
        const hex = accentColor.replace('#', '')
        const r = parseInt(hex.slice(0, 2), 16) / 255
        const g = parseInt(hex.slice(2, 4), 16) / 255
        const b = parseInt(hex.slice(4, 6), 16) / 255
        const max = Math.max(r, g, b)
        const min = Math.min(r, g, b)
        let h = 0
        const l = (max + min) / 2
        const s = max === min ? 0 : (max - min) / (1 - Math.abs(2 * l - 1))
        if (max !== min) {
          if (max === r) h = ((g - b) / (max - min) + (g < b ? 6 : 0)) * 60
          else if (max === g) h = ((b - r) / (max - min) + 2) * 60
          else h = ((r - g) / (max - min) + 4) * 60
        }
        const hover = `hsl(${h} ${Math.round(s * 100)}% ${Math.min(100, Math.round(l * 100) + 10)}%)`
        const press = `hsl(${h} ${Math.round(s * 100)}% ${Math.max(0, Math.round(l * 100) - 8)}%)`
        const dim = `hsla(${h} ${Math.round(s * 100)}% ${Math.round(l * 100)}% / 0.14)`
        const glow = `hsla(${h} ${Math.round(s * 100)}% ${Math.round(l * 100)}% / 0.22)`
        const text = `hsl(${h} ${Math.round(s * 100)}% ${Math.min(100, Math.round(l * 100) + 22)}%)`
        const deep = `hsl(${h} ${Math.round(s * 100)}% ${Math.max(0, Math.round(l * 100) - 22)}%)`
        root.style.setProperty('--color-accent-hover', hover)
        root.style.setProperty('--color-accent-press', press)
        root.style.setProperty('--color-accent-dim', dim)
        root.style.setProperty('--color-accent-glow', glow)
        root.style.setProperty('--color-accent-text', text)
        root.style.setProperty('--color-accent-deep', deep)
      } catch {
        // If the color value is malformed, fall back to just the base.
      }
    }
  }, [accentColor])

  if (loading) {
    return (
      <WindowFrame>
        <div className="absolute inset-0 flex flex-col items-center justify-center bg-void overflow-hidden">
          <div className="absolute inset-0 -z-10">
            <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full bg-accent/8 blur-[120px]" />
          </div>
          <motion.div
            initial={{ scale: 0.9, opacity: 0 }}
            animate={{ scale: 1, opacity: 1 }}
            transition={{ duration: 0.5, ease: [0.16, 1, 0.3, 1] }}
            className="relative w-12 h-12 mb-4"
          >
            <div className="absolute inset-0 rounded-xl bg-accent-glow blur-md" />
            <div className="relative w-full h-full rounded-xl bg-gradient-to-br from-accent to-accent-press grid place-items-center text-void font-bold text-xl font-display">
              j
            </div>
          </motion.div>
          <div className="flex items-center gap-2 text-text-3 text-[12.5px] font-medium">
            <span className="w-1 h-1 rounded-full bg-accent animate-pulse" />
            Connecting to your server
          </div>
        </div>
      </WindowFrame>
    )
  }

  if (!auth) {
    return (
      <WindowFrame>
        <LoginPage onLogin={setAuth} />
      </WindowFrame>
    )
  }

  return (
    <ErrorBoundary>
      <AnimatePresence mode="wait">
        <Routes location={location} key={location.pathname}>
          {/* AppShell renders its own complete chrome (AppHeader includes
              window controls + drag region), so it doesn't get wrapped in
              WindowFrame - that would stack two titlebars. */}
          <Route element={<AppShell auth={auth} onLogout={() => { jellyfinClient.logout(); setAuth(null) }} />}>
            <Route index element={<PageMotion><LandingRedirect /></PageMotion>} />
            <Route path="movies" element={<PageMotion><LibraryPage type="movies" /></PageMotion>} />
            <Route path="shows" element={<PageMotion><LibraryPage type="shows" /></PageMotion>} />
            <Route path="music" element={<PageMotion><MusicPage /></PageMotion>} />
            <Route path="playlists" element={<PageMotion><PlaylistsPage /></PageMotion>} />
            <Route path="item/:id" element={<PageMotion><DetailPage /></PageMotion>} />
            <Route path="person/:id" element={<PageMotion><PersonPage /></PageMotion>} />
            <Route path="collection/:id" element={<PageMotion><CollectionPage /></PageMotion>} />
            <Route path="search" element={<PageMotion><SearchPage /></PageMotion>} />
            <Route path="discover" element={<PageMotion><DiscoverPage /></PageMotion>} />
            <Route path="profile" element={<PageMotion><ProfilePage /></PageMotion>} />
            <Route path="stats" element={<PageMotion><StatsPage /></PageMotion>} />
            <Route path="duplicates" element={<PageMotion><DuplicatesPage /></PageMotion>} />
            <Route path="downloads" element={<PageMotion><DownloadsPage /></PageMotion>} />
            <Route path="live" element={<PageMotion><LiveTvPage /></PageMotion>} />
            <Route path="requests" element={<PageMotion><RequestsPage /></PageMotion>} />
            <Route path="settings" element={<PageMotion><SettingsPage /></PageMotion>} />
            <Route path="*" element={<Navigate to="/" replace />} />
          </Route>
          {/* Player needs the simple Titlebar so the user can still close
              the window during playback. */}
          <Route
            path="play/:id"
            element={
              <WindowFrame>
                <Suspense fallback={<PageFallback />}>
                  <PlayerPage />
                </Suspense>
              </WindowFrame>
            }
          />
        </Routes>
      </AnimatePresence>
    </ErrorBoundary>
  )
}
