import { useEffect, useRef, useState } from 'react'
import {
  Input,
  Button,
  Caption1,
  Subtitle2,
  Skeleton,
  SkeletonItem,
  makeStyles,
  mergeClasses,
  tokens
} from '@fluentui/react-components'
import { SearchRegular } from '@fluentui/react-icons'
import { useSettings } from '../store/settings'
import { ScreenHeader, useScreenStyles } from '../components/ui/Screen'
import { SPACE } from '../components/ui/tokens'
import { DownloadsCard } from './settings/DownloadsCard'
import { AppearanceCard } from './settings/AppearanceCard'
import { PostProcessingCard } from './settings/PostProcessingCard'
import { NetworkCard } from './settings/NetworkCard'
import { CookiesCard } from './settings/CookiesCard'
import { CustomCommandsCard } from './settings/CustomCommandsCard'
import { MediaProfilesCard } from './settings/MediaProfilesCard'
import { FilenamesCard } from './settings/FilenamesCard'
import { BackupCard } from './settings/BackupCard'
import { DiagnosticsCard } from './settings/DiagnosticsCard'
import { SoftwareUpdateCard } from './settings/SoftwareUpdateCard'
import { AboutCard } from './settings/AboutCard'

const useStyles = makeStyles({
  root: {
    display: 'flex',
    flexDirection: 'column',
    gap: SPACE.section
  },
  // The section jump nav (UX7): a row of anchor buttons under the search box.
  sectionNav: {
    display: 'flex',
    gap: SPACE.xtight,
    flexWrap: 'wrap'
  },
  sectionHeading: {
    color: tokens.colorNeutralForeground3,
    paddingTop: SPACE.tight
  },
  section: {
    display: 'flex',
    flexDirection: 'column',
    gap: SPACE.section,
    // Anchor scrolling lands the heading just below the screen padding.
    scrollMarginTop: SPACE.section
  }
})

type CardComponent = () => React.JSX.Element

/**
 * The cards, grouped into stable sections (UX7) with a jump nav — Settings used
 * to be one unsegmented ~11-card scroll. Related cards are co-located: the
 * Downloads section holds the format defaults AND the post-processing card so
 * the two halves of "format" sit together (UX8). Cards render inside
 * React-owned wrappers so the search filter controls each card's visibility via
 * state instead of mutating the card's own DOM `style` (M14).
 */
const SECTIONS: { id: string; title: string; cards: CardComponent[] }[] = [
  {
    id: 'downloads',
    title: 'Downloads',
    cards: [DownloadsCard, PostProcessingCard, FilenamesCard]
  },
  { id: 'appearance', title: 'Appearance', cards: [AppearanceCard] },
  { id: 'network', title: 'Network & accounts', cards: [NetworkCard, CookiesCard] },
  { id: 'advanced', title: 'Advanced', cards: [CustomCommandsCard, MediaProfilesCard] },
  {
    id: 'app',
    title: 'App & maintenance',
    cards: [BackupCard, DiagnosticsCard, SoftwareUpdateCard, AboutCard]
  }
]

// Flat list in render order — the search filter indexes cards globally.
const CARDS: CardComponent[] = SECTIONS.flatMap((s) => s.cards)
// Each section's [start, end) range into CARDS.
const SECTION_RANGES = SECTIONS.map((s, si) => {
  const start = SECTIONS.slice(0, si).reduce((n, x) => n + x.cards.length, 0)
  return { start, end: start + s.cards.length }
})

export function SettingsView(): React.JSX.Element {
  const styles = useStyles()
  const screen = useScreenStyles()
  // Until persisted settings arrive, the cards would flash their fallback values;
  // show skeleton cards for that brief boot window instead (UX26).
  const loaded = useSettings((s) => s.loaded)

  // Settings search (Phase O). Matching still reads each card's rendered text (so a
  // search covers every visible label without hand-maintaining per-card keywords),
  // but the result drives a `hidden` state array on the wrappers — React owns the
  // visibility, rather than us writing `display:none` onto React's own card nodes (M14).
  const [search, setSearch] = useState('')
  const cardRefs = useRef<(HTMLDivElement | null)[]>([])
  const sectionRefs = useRef<(HTMLDivElement | null)[]>([])
  const [hidden, setHidden] = useState<boolean[]>(() => CARDS.map(() => false))

  useEffect(() => {
    const q = search.trim().toLowerCase()
    setHidden(
      CARDS.map((_, i) => {
        if (!q) return false
        const text = (cardRefs.current[i]?.textContent ?? '').toLowerCase()
        return !text.includes(q)
      })
    )
  }, [search])

  const noResults = search.trim() !== '' && hidden.length > 0 && hidden.every(Boolean)
  const searching = search.trim() !== ''

  function jumpTo(si: number): void {
    sectionRefs.current[si]?.scrollIntoView({ behavior: 'smooth', block: 'start' })
  }

  return (
    <div className={screen.page}>
      <div className={mergeClasses(styles.root, screen.width)}>
        <ScreenHeader
          title="Settings"
          description="Folders, formats, network, cookies, and more."
        />
        <div>
          <Input
            input={{ 'aria-label': 'Search settings' }}
            value={search}
            onChange={(_, d) => setSearch(d.value)}
            placeholder="Search settings…"
            contentBefore={<SearchRegular />}
            style={{ width: '100%' }}
          />
          {noResults && (
            <Caption1 style={{ color: tokens.colorNeutralForeground3, marginTop: SPACE.tight }}>
              No settings match &quot;{search}&quot;.
            </Caption1>
          )}
        </div>

        {/* Section jump nav (UX7). Hidden while searching — the filter is the finder then. */}
        {loaded && !searching && (
          <nav className={styles.sectionNav} aria-label="Settings sections">
            {SECTIONS.map((s, si) => (
              <Button key={s.id} size="small" appearance="subtle" onClick={() => jumpTo(si)}>
                {s.title}
              </Button>
            ))}
          </nav>
        )}

        {!loaded ? (
          // Boot skeleton (UX26): three placeholder cards while persisted settings
          // load, instead of a flash of fallback values.
          <Skeleton aria-label="Loading settings">
            {[0, 1, 2].map((i) => (
              <SkeletonItem key={i} size={128} style={{ marginBottom: SPACE.section }} />
            ))}
          </Skeleton>
        ) : (
          SECTIONS.map((section, si) => {
            const range = SECTION_RANGES[si]!
            // A section disappears with all of its cards during a search.
            const sectionHidden = section.cards.every((_, ci) => hidden[range.start + ci])
            return (
              <div
                key={section.id}
                ref={(el) => {
                  sectionRefs.current[si] = el
                }}
                className={styles.section}
                style={sectionHidden ? { display: 'none' } : undefined}
              >
                <Subtitle2 as="h2" className={styles.sectionHeading}>
                  {section.title}
                </Subtitle2>
                {section.cards.map((Card, ci) => {
                  const i = range.start + ci
                  return (
                    <div
                      // Static arrays, never reordered/filtered (cards only hide) —
                      // index keys are stable.
                      key={ci}
                      ref={(el) => {
                        cardRefs.current[i] = el
                      }}
                      style={hidden[i] ? { display: 'none' } : undefined}
                    >
                      <Card />
                    </div>
                  )
                })}
              </div>
            )
          })
        )}
      </div>
    </div>
  )
}
