import { useEffect, useRef, useState } from 'react'
import { NavLink, useLocation, useNavigate } from 'react-router-dom'

import { motion, AnimatePresence } from 'framer-motion'
import {
  Search,
  ArrowLeft,
  Pin,
  PinFilled,
  Minus,
  Square,
  RestoreDown,
  X,
  Bell,
  Film,
  Tv,
} from '../../lib/icons'
import { isTauri } from '../../lib/tauri'
import { jellyfinClient, getItemsApi, getImageUrl, getStoredServerUrl } from '../../api/jellyfin'

/**
 * The single chrome bar at the top of the in-app shell. Replaces the
 * previous trio of (1) Tauri titlebar with brand + window controls, (2)
 * sidebar brand row, and (3) TopBar with back button + page title +
 * search. One bar covers all of them.
 *
 * The whole thing is a Tauri drag region, so the user can grab any empty
 * stretch (logo, title, padding) to move the window. Buttons opt out
 * automatically since they sit on top.
 */

const TOP_LEVEL_PATHS = new Set([
  '/',
  '/movies',
  '/shows',
  '/playlists',
  '/music',
  '/search',
  '/discover',
  '/settings',
])

function pageTitleFor(pathname: string): string {
  if (pathname === '/') return 'Home'
  if (pathname === '/movies') return 'Movies'
  if (pathname === '/shows') return 'TV Shows'
  if (pathname === '/playlists') return 'Playlists'
  if (pathname === '/music') return 'Music'
  if (pathname === '/search') return 'Search'
  if (pathname === '/discover') return 'Discover'
  if (pathname === '/settings') return 'Settings'
  return ''
}

interface Props {
  pinned: boolean
  onTogglePin: () => void
}

export default function AppHeader({ pinned, onTogglePin }: Props) {
  const navigate = useNavigate()
  const location = useLocation()
  const [scrolled, setScrolled] = useState(false)
  const [maximized, setMaximized] = useState(false)
  const [notifsOpen, setNotifsOpen] = useState(false)
  const [notifs, setNotifs] = useState<any[]>([])
  const [lastOpenedAt, setLastOpenedAt] = useState<string | null>(() => {
    try { return localStorage.getItem('jf_bell_opened') } catch { return null }
  })
  const notifsRef = useRef<HTMLDivElement>(null)

  // Poll recently-added episodes + movies for the notification bell.
  // Episodes are deduplicated by series so a full-season drop doesn't
  // flood the list -- only the newest episode per series is shown.
  useEffect(() => {
    const api = jellyfinClient.getApi()
    if (!api) return
    async function poll() {
      try {
        const auth = jellyfinClient.getAuthState()
        if (!auth?.userId) return
        const [movieRes, episodeRes] = await Promise.all([
          getItemsApi(api!).getItems({
            userId: auth.userId,
            sortBy: ['DateCreated'],
            sortOrder: ['Descending'],
            limit: 10,
            recursive: true,
            includeItemTypes: ['Movie'],
            fields: ['DateCreated', 'PrimaryImageAspectRatio', 'ImageTags'] as any[],
          }),
          getItemsApi(api!).getItems({
            userId: auth.userId,
            sortBy: ['DateCreated'],
            sortOrder: ['Descending'],
            limit: 50,
            recursive: true,
            includeItemTypes: ['Episode'],
            fields: ['DateCreated', 'PrimaryImageAspectRatio', 'ImageTags', 'SeriesName', 'SeriesId', 'SeriesPrimaryImageTag', 'ParentIndexNumber', 'IndexNumber'] as any[],
          }),
        ])
        const movies = movieRes.data.Items || []
        const episodes = episodeRes.data.Items || []
        // Deduplicate episodes by series -- keep only the newest per show
        const seenSeries = new Set<string>()
        const dedupedEpisodes: any[] = []
        for (const ep of episodes) {
          const sid = ep.SeriesId || ep.SeriesName
          if (!sid || seenSeries.has(sid)) continue
          seenSeries.add(sid)
          dedupedEpisodes.push(ep)
        }
        // Interleave: one movie, one episode, one movie... capped at 15
        const interleaved: any[] = []
        let m = 0, e = 0
        while (interleaved.length < 15 && (m < movies.length || e < dedupedEpisodes.length)) {
          if (m < movies.length) interleaved.push(movies[m++])
          if (interleaved.length >= 15) break
          if (e < dedupedEpisodes.length) interleaved.push(dedupedEpisodes[e++])
        }
        setNotifs(interleaved)
      } catch { /* ignore */ }
    }
    poll()
    const id = setInterval(poll, 60_000)
    return () => clearInterval(id)
  }, [])

  // Click outside to close
  useEffect(() => {
    if (!notifsOpen) return
    function onDocClick(e: MouseEvent) {
      if (!notifsRef.current?.contains(e.target as Node)) {
        setNotifsOpen(false)
      }
    }
    document.addEventListener('mousedown', onDocClick)
    return () => document.removeEventListener('mousedown', onDocClick)
  }, [notifsOpen])

  const hasUnread = notifs.some(n => {
    const created = n.DateCreated
    if (!created) return false
    if (!lastOpenedAt) return true
    return created > lastOpenedAt
  })

  // Subtle background fade in when the main pane is scrolled - same UX
  // affordance the old TopBar had, just on the unified header.
  useEffect(() => {
    const main = document.querySelector('main.content-scroll') as HTMLElement | null
    if (!main) {
      setScrolled(false)
      return
    }
    const onScroll = () => setScrolled(main.scrollTop > 4)
    main.addEventListener('scroll', onScroll, { passive: true })
    onScroll()
    return () => main.removeEventListener('scroll', onScroll)
  }, [location.pathname])

  // Ctrl+K opens search
  useEffect(() => {
    function onKey(e: KeyboardEvent) {
      if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
        e.preventDefault()
        navigate('/search')
      }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [navigate])

  // Track maximize state so the maximize icon flips to a restore icon when
  // the window is full-screen. Only meaningful in Tauri.
  useEffect(() => {
    if (!isTauri) return
    let cancelled = false
    let unlisten: (() => void) | undefined
    import('@tauri-apps/api/window').then(({ getCurrentWindow }) => {
      if (cancelled) return
      const win = getCurrentWindow()
      win.isMaximized().then(v => {
        if (!cancelled) setMaximized(v)
      }).catch(() => {})
      win.onResized(async () => {
        const v = await win.isMaximized()
        if (!cancelled) setMaximized(v)
      }).then(u => {
        if (cancelled) u()
        else unlisten = u
      }).catch(() => {})
    })
    return () => {
      cancelled = true
      unlisten?.()
    }
  }, [])

  function callWin(action: 'minimize' | 'toggleMaximize' | 'close') {
    if (!isTauri) return
    import('@tauri-apps/api/window').then(({ getCurrentWindow }) => {
      const win = getCurrentWindow()
      if (action === 'minimize') win.minimize()
      else if (action === 'toggleMaximize') win.toggleMaximize()
      else win.close()
    })
  }

  const showBack = !TOP_LEVEL_PATHS.has(location.pathname)
  const title = pageTitleFor(location.pathname)

  return (
    <header
      data-tauri-drag-region
      className={`app-header relative shrink-0 h-11 flex items-stretch z-sticky border-b transition-colors duration-300 ease-out ${
        scrolled
          ? 'bg-glass-strong backdrop-blur-xl border-border'
          : 'bg-void/95 border-border/60'
      }`}
    >
      {/* ── Left: brand + pin ──────────────────────────────────── */}
      <div data-tauri-drag-region className="flex items-center gap-2 pl-3 pr-2">
        <NavLink
          to="/"
          aria-label="Home"
          className="group flex items-center gap-2.5 focus-ring rounded-md px-1 py-1 -mx-1 hover:bg-glass-light transition-colors"
        >
          <div className="relative w-6 h-6 shrink-0">
            <div className="absolute inset-0 rounded-md bg-gradient-to-br from-accent to-accent-press" />
            <div className="absolute inset-0 rounded-md bg-accent-glow blur-[6px] opacity-50" />
            <div className="relative w-full h-full rounded-md grid place-items-center overflow-hidden">
              <span
                aria-hidden
                className="block w-[64%] h-[64%] bg-void"
                style={{
                  WebkitMaskImage: 'url(/icon.svg)',
                  maskImage: 'url(/icon.svg)',
                  WebkitMaskRepeat: 'no-repeat',
                  maskRepeat: 'no-repeat',
                  WebkitMaskPosition: 'center',
                  maskPosition: 'center',
                  WebkitMaskSize: 'contain',
                  maskSize: 'contain',
                }}
              />
            </div>
          </div>
          <span className="hidden sm:flex flex-col leading-none">
            <span className="text-[11.5px] font-bold tracking-[0.18em] text-text-1 font-display">
              JELLYBLOOM
            </span>
            <span className="text-[8.5px] uppercase tracking-[0.2em] font-semibold text-text-4 mt-0.5">
              Media client
            </span>
          </span>
        </NavLink>

        <button
          type="button"
          onClick={onTogglePin}
          aria-label={pinned ? 'Unpin sidebar' : 'Pin sidebar open'}
          aria-pressed={pinned}
          title={pinned ? 'Unpin sidebar' : 'Pin sidebar open'}
          className={`w-7 h-7 grid place-items-center rounded-md transition-colors duration-150 focus-ring ${
            pinned
              ? 'text-accent bg-accent-dim hover:bg-accent/20'
              : 'text-text-3 hover:text-text-1 hover:bg-glass-light'
          }`}
        >
          {pinned ? <PinFilled size={13} /> : <Pin size={13} stroke={2} />}
        </button>
      </div>

      {/* ── Middle: back button + page title (drag region) ─────── */}
      <div data-tauri-drag-region className="flex-1 min-w-0 flex items-center gap-2 px-3">
        {showBack && (
          <button
            onClick={() => navigate(-1)}
            className="w-7 h-7 grid place-items-center rounded-md text-text-3 hover:text-text-1 hover:bg-glass-light transition-colors duration-150 focus-ring shrink-0"
            aria-label="Back"
          >
            <ArrowLeft size={15} stroke={2} />
          </button>
        )}
        <AnimatePresence mode="wait">
          {title && (
            <motion.h1
              data-tauri-drag-region
              key={title}
              initial={{ opacity: 0, y: 4 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -4 }}
              transition={{ duration: 0.22, ease: [0.16, 1, 0.3, 1] }}
              className="text-[13px] font-semibold text-text-1 tracking-tight truncate"
            >
              {title}
            </motion.h1>
          )}
        </AnimatePresence>
      </div>

      {/* ── Right: search + window controls ─────────────────────── */}
      <div className="flex items-stretch">
        <button
          onClick={() => navigate('/search')}
          className="group self-center mr-2 flex items-center gap-2 h-7 pl-2 pr-1.5 rounded-md bg-elevated/50 hover:bg-elevated border border-border hover:border-border-hover transition-all duration-200 ease-out text-text-3 hover:text-text-2 focus-ring"
          aria-label="Search"
        >
          <Search size={12} strokeWidth={2} className="text-text-3 group-hover:text-accent transition-colors duration-200" />
          <span className="text-[11px] hidden md:inline">Search anything</span>
          <kbd className="hidden md:inline-flex items-center gap-0.5 ml-1 px-1.5 h-4 rounded text-[9.5px] font-mono text-text-4 bg-void/60 border border-border">
            <span className="text-[8px]">^</span>K
          </kbd>
        </button>

        {/* Notification bell — new releases only */}
        <div ref={notifsRef} className="relative self-center mr-2">
          <button
            onClick={() => {
              setNotifsOpen(o => {
                const next = !o
                if (next) {
                  const now = new Date().toISOString()
                  setLastOpenedAt(now)
                  try { localStorage.setItem('jf_bell_opened', now) } catch { /* noop */ }
                }
                return next
              })
            }}
            className="relative w-7 h-7 grid place-items-center rounded-md text-text-3 hover:text-text-1 hover:bg-elevated transition-colors focus-ring"
            aria-label="New releases"
          >
            <Bell size={13} stroke={2} />
            {hasUnread && (
              <span className="absolute top-0.5 right-0.5 w-1.5 h-1.5 rounded-full bg-accent" />
            )}
          </button>
          <AnimatePresence>
            {notifsOpen && (
              <motion.div
                initial={{ opacity: 0, y: 6, scale: 0.98 }}
                animate={{ opacity: 1, y: 0, scale: 1 }}
                exit={{ opacity: 0, y: 6, scale: 0.98 }}
                transition={{ duration: 0.18 }}
                className="absolute right-0 top-9 w-80 rounded-xl bg-glass-strong backdrop-blur-2xl border border-white/10 shadow-[0_20px_60px_-15px_rgba(0,0,0,0.85)] z-[9999] overflow-hidden"
              >
                <div className="px-3 py-2.5 border-b border-white/8 flex items-center justify-between">
                  <p className="text-[11px] font-semibold text-text-1 tracking-tight">New releases</p>
                  {notifs.length > 0 && (
                    <span className="text-[10px] text-text-4 tabular-nums">{notifs.length} new</span>
                  )}
                </div>
                <div className="max-h-72 overflow-y-auto content-scroll">
                  {notifs.length === 0 ? (
                    <p className="px-3 py-4 text-[11.5px] text-text-3 text-center">Nothing new yet</p>
                  ) : (
                    notifs.map((entry: any) => {
                      const isUnread = !lastOpenedAt || (entry.DateCreated && entry.DateCreated > lastOpenedAt)
                      const isEpisode = entry.Type === 'Episode'
                      const seriesName = entry.SeriesName
                      const season = entry.ParentIndexNumber
                      const epNum = entry.IndexNumber
                      const epLabel = isEpisode && (season != null || epNum != null)
                        ? `S${season ?? '?'}E${epNum ?? '?'}${entry.Name ? ` · ${entry.Name}` : ''}`
                        : null

                      // Poster thumbnail: series poster for episodes, movie poster for movies
                      const serverUrl = getStoredServerUrl()
                      const posterUrl = (() => {
                        if (!serverUrl) return null
                        if (isEpisode && entry.SeriesId && entry.SeriesPrimaryImageTag) {
                          return getImageUrl(serverUrl, entry.SeriesId, 'Primary', 160, entry.SeriesPrimaryImageTag)
                        }
                        if (entry.ImageTags?.Primary) {
                          return getImageUrl(serverUrl, entry.Id, 'Primary', 160, entry.ImageTags.Primary)
                        }
                        return null
                      })()

                      return (
                        <button
                          key={entry.Id}
                          onClick={() => {
                            if (entry.Id) {
                              navigate(`/item/${entry.Id}`)
                              setNotifsOpen(false)
                            }
                          }}
                          className={`w-full text-left px-3 py-2.5 text-[11.5px] border-b border-white/5 last:border-0 hover:bg-white/4 transition-colors flex items-start gap-3 ${isUnread ? 'bg-white/[0.03]' : ''}`}
                        >
                          {posterUrl ? (
                            <img
                              src={posterUrl}
                              alt=""
                              className="w-8 aspect-[2/3] rounded object-cover shrink-0 bg-void"
                              loading="lazy"
                            />
                          ) : isEpisode ? (
                            <Tv size={13} className="text-accent shrink-0 mt-0.5" />
                          ) : (
                            <Film size={13} className="text-accent shrink-0 mt-0.5" />
                          )}
                          <div className="min-w-0 flex-1">
                            {isEpisode && seriesName ? (
                              <>
                                <p className="text-text-2 truncate font-medium">{seriesName}</p>
                                {epLabel && (
                                  <p className="text-text-3 truncate">{epLabel}</p>
                                )}
                              </>
                            ) : (
                              <p className="text-text-2 truncate font-medium">{entry.Name}</p>
                            )}
                            <p className="text-text-4 tabular-nums mt-0.5">
                              {entry.DateCreated ? new Date(entry.DateCreated).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : ''}
                              {entry.ProductionYear && !isEpisode ? ` · ${entry.ProductionYear}` : ''}
                            </p>
                          </div>
                          {isUnread && (
                            <span className="w-1.5 h-1.5 rounded-full bg-accent shrink-0 mt-1.5" />
                          )}
                        </button>
                      )
                    })
                  )}
                </div>
              </motion.div>
            )}
          </AnimatePresence>
        </div>

        {isTauri && (
          <>
            <WinButton onClick={() => callWin('minimize')} aria-label="Minimize">
              <Minus size={14} stroke={2} />
            </WinButton>
            <WinButton
              onClick={() => callWin('toggleMaximize')}
              aria-label={maximized ? 'Restore' : 'Maximize'}
            >
              {maximized ? <RestoreDown size={12} stroke={2} /> : <Square size={12} stroke={2} />}
            </WinButton>
            <WinButton onClick={() => callWin('close')} aria-label="Close" variant="close">
              <X size={14} stroke={2.25} />
            </WinButton>
          </>
        )}
      </div>
    </header>
  )
}

function WinButton({
  children,
  variant,
  ...props
}: React.ButtonHTMLAttributes<HTMLButtonElement> & { variant?: 'close' }) {
  return (
    <button
      {...props}
      className={`w-11 h-full grid place-items-center text-text-3 transition-colors duration-100 focus-ring ${
        variant === 'close'
          ? 'hover:bg-red-500 hover:text-white'
          : 'hover:bg-elevated hover:text-text-1'
      }`}
    >
      {children}
    </button>
  )
}
