import type { CSSProperties, Dispatch, FC, KeyboardEvent, SetStateAction } from 'react'

import { useEffect, useMemo, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useLocation, useNavigate } from 'react-router-dom'

import FieldError from '@/components/common/FieldError'
import FormField from '@/components/common/FormField'
import Label from '@/components/common/Label'
import PageTitle from '@/components/common/PageTitle'
import MainArea from '@/components/layout/MainArea'
import TagLink from '@/components/TagLink'
import InheritDialogue from '@/components/users/InheritDialogue'
import UserCodeDialogue from '@/components/users/UserCodeDialogue'
import { CATEGORY_NAMES, CATEGORIES } from '@/consts'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import { SITE_TITLE } from '@/config'
import { apiPut } from '@/lib/api'
import {
    applyThemeSelection,
    BUILTIN_THEMES,
    createCustomThemeFromBase,
    defaultThemeTagColoursFor,
    DEFAULT_USER_SETTINGS,
    deriveUserThemeModeFromSelection,
    fetchUserSettings,
    hasStoredClientThemeSelection,
    getClientActiveThemeId,
    getClientCustomThemes,
    getResolvedThemeFromSelection,
    setClientAppearanceThemes,
    updateUserSettings,
} from '@/lib/settings'
import { cn, inputClass } from '@/lib/utils'
import { useValidationErrors } from '@/lib/useValidationErrors'

import type { Category, Tag, User } from '@/types'
import type {
    ActiveThemeId,
    ClientTheme,
    CustomClientTheme,
    ThemeTagColours,
    UserSettings,
} from '@/lib/settings'

type Props = {
    user:     User | null
    setUser:  Dispatch<SetStateAction<User | null>>
}

type UserFormField = 'name'
type SettingsFormField = 'theme'
type SettingsTab = 'account' | 'theme' | 'keyboard'

type TabSpec = {
    id:     SettingsTab
    label:  string }

type SharedSectionProps = {
    user:                  User
    name:                  string
    onNameChange:          (value: string) => void
    onInheritOpen:         () => void
    onUserCodeOpen:        () => void
    onUserSubmit:          () => void
    userBaseErrors:        string[]
    userFieldErrors:       Partial<Record<UserFormField, string[]>>
    draftActiveThemeId:    ActiveThemeId
    draftCustomThemes:     Record<string, CustomClientTheme>
    selectedTheme:         ClientTheme
    draftTagColours:       ThemeTagColours
    onThemeChange:         (themeId: ActiveThemeId) => void
    onEnsureEditableTheme: () => void
    onTagColourChange:     (category: Category, colour: string) => void
    onResetAllTagColours:  () => void
    onResetTagColour:      (category: Category) => void
    onThemeSubmit:         () => void
    settingsBaseErrors:    string[]
    settingsFieldErrors:   Partial<Record<SettingsFormField, string[]>>
}

const tabs: TabSpec[] = [
    { id: 'account', label: 'アカウント' },
    { id: 'theme', label: 'テーマ' },
    { id: 'keyboard', label: 'キーボード' },
]

const sectionClassName =
    'space-y-4 rounded-xl border border-border bg-background/80 p-4'

const themeLabel: Record<UserSettings['theme'], string> = {
    system: 'システム設定に従う',
    light:  'ライト・モード',
    dark:   'ダーク・モード',
}

const builtinThemeLabel: Record<'light' | 'dark', string> = {
    light: 'ライト・モード',
    dark:  'ダーク・モード',
}

const keyboardShortcutRows = [
    {
	action:   '投稿詳細を開く',
	key:      'Enter',
	scope:    '一覧・検索結果',
	conflict: 'なし',
    },
    {
	action:   '候補タグを選ぶ',
	key:      '↑ / ↓ / Enter',
	scope:    'タグ入力',
	conflict: 'なし',
    },
    {
	action:   '候補タグを閉じる',
	key:      'Escape',
	scope:    'タグ入力',
	conflict: 'なし',
    },
]

const previewTag = (
    id:         number,
    name:       string,
    category:   Category,
    postCount:  number,
    hasWiki:    boolean,
    options?:   { hasDeerjikists?: boolean },
): Tag => ({
    id,
    name,
    category,
    deprecatedAt: null,
    aliases: [],
    parents: [],
    postCount,
    createdAt: '',
    updatedAt: '',
    hasWiki,
    materialId: null,
    hasDeerjikists: options?.hasDeerjikists ?? false,
    children: [],
})

const previewTags: Record<Category, Tag> = {
    deerjikist: previewTag (1, 'にじらー', 'deerjikist', 12, true, { hasDeerjikists: true }),
    meme: previewTag (2, 'ミーム', 'meme', 34, true),
    character: previewTag (3, 'キャラクター', 'character', 56, false),
    general: previewTag (4, '一般', 'general', 78, true),
    material: previewTag (5, '素材', 'material', 90, false),
    meta: previewTag (6, 'メタ', 'meta', 21, true),
    nico: previewTag (7, 'nico:sm9', 'nico', 43, false),
}


const parseTab = (search: string): SettingsTab => {
  const tab = new URLSearchParams (search).get ('tab')
  return tabs.some (candidate => candidate.id === tab)
    ? tab as SettingsTab
    : 'account'
}


const tabButtonId = (tab: SettingsTab): string => `settings-tab-${ tab }`
const tabPanelId = (tab: SettingsTab): string => `settings-panel-${ tab }`


const themePreviewStyle = (tagColours: ThemeTagColours): CSSProperties =>
    Object.fromEntries (
	CATEGORIES.flatMap (category => [
	    [`--tag-colour-${ category }`, tagColours[category]],
	    [`--tag-colour-${ category }-hover`, tagColours[category]],
	]),
    ) as CSSProperties


const themeLabelById = (
    themeId:       ActiveThemeId,
    customThemes:  Record<string, CustomClientTheme>,
): string => {
  if (themeId === 'system')
    return themeLabel.system
  if (themeId === 'light' || themeId === 'dark')
    return builtinThemeLabel[themeId]
  return (customThemes[themeId] ?? BUILTIN_THEMES.light).name
}


const AccountSection: FC<SharedSectionProps> = ({
    name,
    onInheritOpen,
    onNameChange,
    onUserCodeOpen,
    onUserSubmit,
    user,
    userBaseErrors,
    userFieldErrors,
}) => (
    <section className={sectionClassName}>
      <h2 className="text-xl font-bold">アカウント</h2>
      <FieldError messages={userBaseErrors}/>

      <FormField label="表示名" messages={userFieldErrors.name}>
	{({ describedBy, invalid }) => (
	    <>
	      <input
		  type="text"
		  aria-describedby={describedBy}
		  aria-invalid={invalid}
		  className={inputClass (invalid)}
		  value={name}
		  placeholder="名もなきニジラー"
		  onChange={ev => onNameChange (ev.target.value)}/>
	      {!(user.name) && (
		  <p className="mt-1 text-sm text-red-500">
		    名前が未設定のアカウントは 30 日間アクセスしないと削除されます!!!!
		  </p>)}
	    </>)}
      </FormField>

      <div className="flex flex-wrap gap-2">
	<Button type="button" onClick={onUserSubmit}>
	  表示名を更新
	</Button>
      </div>

      <div className="space-y-2">
	<Label>引継ぎ</Label>
	<div className="flex flex-wrap gap-2">
	  <Button
	      type="button"
	      onClick={onUserCodeOpen}
	      className="bg-gray-600 text-white"
	      disabled={!(user)}>
	    引継ぎコードを表示
	  </Button>
	  <Button
	      type="button"
	      onClick={onInheritOpen}
	      className="bg-red-600 text-white"
	      disabled={!(user)}>
	    ほかのブラウザから引継ぐ
	  </Button>
	</div>
      </div>
    </section>)


const ThemeSection: FC<SharedSectionProps> = ({
    draftActiveThemeId,
    draftCustomThemes,
    selectedTheme,
    draftTagColours,
    onEnsureEditableTheme,
    onResetAllTagColours,
    onResetTagColour,
    onTagColourChange,
    onThemeChange,
    onThemeSubmit,
    settingsBaseErrors,
    settingsFieldErrors,
}) => (
    <section className={sectionClassName}>
      <h2 className="text-xl font-bold">テーマ</h2>
      <FieldError messages={settingsBaseErrors}/>

      <FormField label="基本テーマ" messages={settingsFieldErrors.theme}>
	{() => (
	    <select
		className={inputClass (Boolean (settingsFieldErrors.theme?.length))}
		value={draftActiveThemeId}
		onChange={ev => onThemeChange (ev.target.value as ActiveThemeId)}>
	      <option value="system">{themeLabel.system}</option>
	      <option value="light">{builtinThemeLabel.light}</option>
	      <option value="dark">{builtinThemeLabel.dark}</option>
	      {Object.values (draftCustomThemes).length > 0 && (
		  <optgroup label="カスタムテーマ">
		    {Object.values (draftCustomThemes).map (theme => (
			<option key={theme.id} value={theme.id}>
			  {theme.name}
			</option>))}
		  </optgroup>)}
	    </select>)}
      </FormField>

      <div className="space-y-2 rounded-lg border border-dashed border-border/70 p-3 text-sm">
	<p>
	  テーマの選択状態は現在のブラウザに保存し，背景・文字色はそのテーマの base に従います．
	</p>
	<p className="text-muted-foreground">
	  backend には system / light / dark の mode だけ保存します．
	  カスタムテーマ本体は今は browser-local です．
	</p>
	<p className="text-muted-foreground">
	  将来は themes テーブルなどへ移して，端末間同期できる構造に拡張できます．
	</p>
      </div>

      <div className="space-y-3">
	<div className="flex flex-wrap items-center justify-between gap-2">
	  <div>
	  <h3 className="font-medium">タグカテゴリ色</h3>
	  <p className="text-sm text-muted-foreground">
	    {selectedTheme.builtin || draftActiveThemeId === 'system'
	     ? '組み込みテーマは直接変更されません．色を変更するとカスタムテーマを作成します．'
	     : `現在の編輯対象: ${ selectedTheme.name }`}
	  </p>
	  </div>
	  <Button
	      type="button"
	      variant="outline"
	      onClick={onResetAllTagColours}
	      disabled={selectedTheme.builtin || draftActiveThemeId === 'system'}>
	    すべてのタグ色を既定に戻す
	  </Button>
	</div>

	<div className="grid gap-3 sm:grid-cols-2">
	  {CATEGORIES.map (category => {
	      const resolvedDefaults =
		  defaultThemeTagColoursFor (selectedTheme.baseTheme)
	      const isDefault =
		  draftTagColours[category] === resolvedDefaults[category]

	      return (
		  <div
		      key={category}
		      className="space-y-2 rounded-lg border border-border/70 px-3 py-2">
		    <div className="flex items-center justify-between gap-3">
		      <span className="text-sm">{CATEGORY_NAMES[category]}</span>
		      <input
			  type="color"
			  value={draftTagColours[category]}
			  onPointerDown={onEnsureEditableTheme}
			  onFocus={onEnsureEditableTheme}
			  onChange={ev => onTagColourChange (category, ev.target.value)}
			  className="h-10 w-16 cursor-pointer rounded border border-border
				     bg-transparent p-1"/>
		    </div>
		    <div className="flex justify-end">
		      <Button
			  type="button"
			  variant="ghost"
			  className="h-auto px-0 py-0 text-xs"
			  onClick={() => onResetTagColour (category)}
			  disabled={
			      isDefault
			      || selectedTheme.builtin
			      || draftActiveThemeId === 'system'
			  }>
			既定に戻す
		      </Button>
		    </div>
		  </div>)
	  })}
	</div>
      </div>

      <div className="space-y-2 rounded-lg border border-border bg-background p-3
		      text-foreground shadow-sm transition-colors">
	<p className="text-sm font-medium">プレビュー</p>
	<p className="text-xs text-muted-foreground">
	  現在の表示テーマ: {themeLabelById (draftActiveThemeId, draftCustomThemes)}
	</p>
	<div className="flex flex-wrap gap-3" style={themePreviewStyle (draftTagColours)}>
	  {CATEGORIES.map (category => (
	      <TagLink
		  key={category}
		  tag={previewTags[category]}
		  linkFlg={false}
		  truncateOnMobile
		  withWiki={false}/>))}
	</div>
      </div>

      <div className="space-y-2 rounded-lg border border-dashed border-border/70 p-3 text-sm">
	<p className="font-medium">既存 backend 設定について</p>
	<p>
	  auto_fetch_title、auto_fetch_thumbnail、wiki_editor_mode は backend 側に
	  保持したままです．この共通設定画面にはまだ接続していません．
	</p>
      </div>

      <Button type="button" onClick={onThemeSubmit}>
	テーマを保存
      </Button>
    </section>)


const KeyboardSection: FC = () => (
    <section className={sectionClassName}>
      <h2 className="text-xl font-bold">キーボード</h2>

      <p className="text-sm text-muted-foreground">
	現段階では一覧表示のみです．将来は action / key / scope / conflict を持つ
	key bind 設定へ拡張できる形にしています．
      </p>

      <div className="overflow-x-auto">
	<table className="w-full min-w-[28rem] table-fixed border-collapse text-sm">
	  <thead className="border-b border-border">
	    <tr>
	      <th className="p-2 text-left">操作</th>
	      <th className="p-2 text-left">キー</th>
	      <th className="p-2 text-left">適用範囲</th>
	      <th className="p-2 text-left">競合</th>
	    </tr>
	  </thead>
	  <tbody>
	    {keyboardShortcutRows.map (row => (
		<tr key={row.action} className="border-b border-border/60 last:border-b-0">
		  <td className="p-2">{row.action}</td>
		  <td className="p-2 font-mono">{row.key}</td>
		  <td className="p-2">{row.scope}</td>
		  <td className="p-2">{row.conflict}</td>
		</tr>))}
	  </tbody>
	</table>
      </div>
    </section>)


const SettingPage: FC<Props> = ({ user, setUser }) => {
  const location = useLocation ()
  const navigate = useNavigate ()

  const [inheritVsbl, setInheritVsbl] = useState (false)
  const [loading, setLoading] = useState (true)
  const [name, setName] = useState ('')
  const [settingsError, setSettingsError] = useState<string | null> (null)
  const [, setDraftSettings] = useState<UserSettings> (DEFAULT_USER_SETTINGS)
  const [savedActiveThemeId, setSavedActiveThemeId] =
      useState<ActiveThemeId> (getClientActiveThemeId ())
  const [savedCustomThemes, setSavedCustomThemes] =
      useState<Record<string, CustomClientTheme>> (getClientCustomThemes ())
  const [draftActiveThemeId, setDraftActiveThemeId] =
      useState<ActiveThemeId> (getClientActiveThemeId ())
  const [draftCustomThemes, setDraftCustomThemes] =
      useState<Record<string, CustomClientTheme>> (getClientCustomThemes ())
  const [userCodeVsbl, setUserCodeVsbl] = useState (false)
  const {
    baseErrors: userBaseErrors,
    fieldErrors: userFieldErrors,
    clearValidationErrors: clearUserValidationErrors,
    applyValidationError: applyUserValidationError,
  } = useValidationErrors<UserFormField> ()
  const {
    baseErrors: settingsBaseErrors,
    fieldErrors: settingsFieldErrors,
    clearValidationErrors: clearSettingsValidationErrors,
    applyValidationError: applySettingsValidationError,
  } = useValidationErrors<SettingsFormField> ()

  const activeTab = useMemo<SettingsTab> (() => parseTab (location.search), [location.search])
  const selectedTheme = useMemo<ClientTheme> (
      () => getResolvedThemeFromSelection (draftActiveThemeId, draftCustomThemes),
      [draftActiveThemeId, draftCustomThemes],
  )
  const draftTagColours = selectedTheme.tokens.tagColours

  const setActiveTab = (tab: SettingsTab) => {
    const params = new URLSearchParams (location.search)
    params.set ('tab', tab)
    navigate (
	`${ location.pathname }?${ params.toString () }`,
	{ replace: true },
    )
  }

  const settingsTabErrors = useMemo<Record<SettingsTab, boolean>> (() => ({
      account: Boolean (userFieldErrors.name?.length),
      theme: Boolean (settingsFieldErrors.theme?.length),
      keyboard: false,
  }), [settingsFieldErrors.theme, userFieldErrors.name])

  const applyDraftThemeSelection = (
      activeThemeId: ActiveThemeId,
      customThemes:  Record<string, CustomClientTheme>,
  ) => {
    setDraftActiveThemeId (activeThemeId)
    setDraftCustomThemes (customThemes)
    setDraftSettings (current => ({
	...current,
	theme: deriveUserThemeModeFromSelection (activeThemeId, customThemes),
    }))
    applyThemeSelection (activeThemeId, customThemes)
  }

  const ensureEditableCustomTheme = (): {
      activeThemeId: ActiveThemeId
      customThemes:  Record<string, CustomClientTheme>
  } => {
    if (draftActiveThemeId !== 'system' && draftCustomThemes[draftActiveThemeId] != null)
      {
	return {
	    activeThemeId: draftActiveThemeId,
	    customThemes: draftCustomThemes,
	}
      }

    const customTheme = createCustomThemeFromBase (
	selectedTheme.baseTheme,
	{
	    ...selectedTheme.tokens,
	    tagColours: { ...selectedTheme.tokens.tagColours },
	},
    )
    const nextCustomThemes = {
	...draftCustomThemes,
	[customTheme.id]: customTheme,
    }

    return {
	activeThemeId: customTheme.id,
	customThemes: nextCustomThemes,
    }
  }

  const handleEnsureEditableTheme = (): {
      activeThemeId: ActiveThemeId
      customThemes:  Record<string, CustomClientTheme>
  } => {
    const editable = ensureEditableCustomTheme ()

    if (editable.activeThemeId !== draftActiveThemeId
	|| editable.customThemes !== draftCustomThemes)
      {
	applyDraftThemeSelection (editable.activeThemeId, editable.customThemes)
      }

    return editable
  }

  const handleTabKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
    const currentIndex = tabs.findIndex (tab => tab.id === activeTab)

    switch (event.key)
    {
    case 'ArrowDown':
    case 'ArrowRight':
      event.preventDefault ()
      setActiveTab (tabs[(currentIndex + 1) % tabs.length].id)
      break

    case 'ArrowUp':
    case 'ArrowLeft':
      event.preventDefault ()
      setActiveTab (tabs[(currentIndex - 1 + tabs.length) % tabs.length].id)
      break

    case 'Home':
      event.preventDefault ()
      setActiveTab (tabs[0].id)
      break

    case 'End':
      event.preventDefault ()
      setActiveTab (tabs[tabs.length - 1].id)
      break
    }
  }

  const handleUserSubmit = async () => {
    if (!(user))
      return

    clearUserValidationErrors ()

    const formData = new FormData
    formData.append ('name', name)

    try
    {
      const data = await apiPut<User> (
	  `/users/${ user.id }`, formData,
	  { headers: { 'Content-Type': 'multipart/form-data' } })
      setUser (currentUser => ({ ...currentUser, ...data }))
      toast ({ title: '表示名を更新しました．' })
    }
    catch (submitError)
    {
      applyUserValidationError (submitError)
      toast ({ title: '表示名を更新できませんでした．' })
    }
  }

  const handleThemeSubmit = async () => {
    clearSettingsValidationErrors ()

    try
    {
      const themeMode =
	  deriveUserThemeModeFromSelection (draftActiveThemeId, draftCustomThemes)
      const data = await updateUserSettings ({ theme: themeMode })
      setDraftSettings ({ ...data, theme: themeMode })
      setSavedActiveThemeId (draftActiveThemeId)
      setSavedCustomThemes (draftCustomThemes)
      setClientAppearanceThemes (draftActiveThemeId, draftCustomThemes)
      applyThemeSelection (draftActiveThemeId, draftCustomThemes)
      toast ({ title: 'テーマ設定を保存しました．' })
    }
    catch (submitError)
    {
      applySettingsValidationError (submitError)
      toast ({ title: 'テーマ設定を保存できませんでした．' })
    }
  }

  useEffect (() => {
    if (!user)
      return

    setName (user.name ?? '')
  }, [user])

  useEffect (() => {
    if (!(user))
      return

    let cancelled = false

    void (async () => {
      setLoading (true)

      try
      {
	const data = await fetchUserSettings ()
	if (cancelled)
	  return

	const customThemes = getClientCustomThemes ()
	const activeThemeId = hasStoredClientThemeSelection ()
	    ? getClientActiveThemeId ()
	    : data.theme
	const themeMode = deriveUserThemeModeFromSelection (activeThemeId, customThemes)

	setDraftSettings ({ ...data, theme: themeMode })
	setSavedActiveThemeId (activeThemeId)
	setSavedCustomThemes (customThemes)
	setDraftActiveThemeId (activeThemeId)
	setDraftCustomThemes (customThemes)
	setSettingsError (null)
      }
      catch
      {
	if (cancelled)
	  return

	setSettingsError ('設定の読込みに失敗しました．')
      }
      finally
      {
	if (!(cancelled))
	  setLoading (false)
      }
    }) ()

    return () => {
      cancelled = true
    }
  }, [user])

  useEffect (() => {
    if (userFieldErrors.name?.length)
      setActiveTab ('account')
  }, [location.pathname, location.search, userFieldErrors.name])

  useEffect (() => {
    if (settingsFieldErrors.theme?.length)
      setActiveTab ('theme')
  }, [location.pathname, location.search, settingsFieldErrors.theme])

  useEffect (() => {
    applyThemeSelection (draftActiveThemeId, draftCustomThemes)

    return () => {
      applyThemeSelection (savedActiveThemeId, savedCustomThemes)
    }
  }, [draftActiveThemeId, draftCustomThemes, savedActiveThemeId, savedCustomThemes])

  const sectionProps: SharedSectionProps | null = user ? {
      user,
      name,
      onNameChange: setName,
      onInheritOpen: () => setInheritVsbl (true),
      onUserCodeOpen: () => setUserCodeVsbl (true),
      onUserSubmit: handleUserSubmit,
      userBaseErrors,
      userFieldErrors,
      draftActiveThemeId,
      draftCustomThemes,
      selectedTheme,
      draftTagColours,
      onThemeChange: themeId => {
	  applyDraftThemeSelection (themeId, draftCustomThemes)
      },
      onEnsureEditableTheme: () => {
	  handleEnsureEditableTheme ()
      },
      onTagColourChange: (category, colour) => {
	  const editable = handleEnsureEditableTheme ()
	  const editableTheme = editable.customThemes[editable.activeThemeId]
	  const nextCustomThemes = {
	      ...editable.customThemes,
	      [editable.activeThemeId]: {
		  ...editableTheme,
		  tokens: {
		      ...editableTheme.tokens,
		      tagColours: {
			  ...editableTheme.tokens.tagColours,
			  [category]: colour,
		      },
		  },
	      },
	  }
	  applyDraftThemeSelection (editable.activeThemeId, nextCustomThemes)
      },
      onResetAllTagColours: () => {
	  if (selectedTheme.builtin || draftActiveThemeId === 'system')
	    return

	  applyDraftThemeSelection (
	      draftActiveThemeId,
	      {
		  ...draftCustomThemes,
		  [draftActiveThemeId]: {
		      ...draftCustomThemes[draftActiveThemeId],
		      tokens: {
			  ...draftCustomThemes[draftActiveThemeId].tokens,
			  tagColours: {
			      ...defaultThemeTagColoursFor (selectedTheme.baseTheme),
			  },
		      },
		  },
	      },
	  )
      },
      onResetTagColour: category => {
	  if (selectedTheme.builtin || draftActiveThemeId === 'system')
	    return

	  applyDraftThemeSelection (
	      draftActiveThemeId,
	      {
		  ...draftCustomThemes,
		  [draftActiveThemeId]: {
		      ...draftCustomThemes[draftActiveThemeId],
		      tokens: {
			  ...draftCustomThemes[draftActiveThemeId].tokens,
			  tagColours: {
			      ...draftCustomThemes[draftActiveThemeId].tokens.tagColours,
			      [category]:
				  defaultThemeTagColoursFor (selectedTheme.baseTheme)[category],
			  },
		      },
		  },
	      },
	  )
      },
      onThemeSubmit: handleThemeSubmit,
      settingsBaseErrors,
      settingsFieldErrors,
  } : null

  return (
      <MainArea>
	<Helmet>
	  <meta name="robots" content="noindex"/>
	  <title>設定 | {SITE_TITLE}</title>
	</Helmet>

	<div className="mx-auto max-w-xl space-y-4 p-4 md:hidden">
	  <PageTitle>設定</PageTitle>
	  <FieldError messages={settingsError ? [settingsError] : []}/>
	  {loading ? 'Loading...' : (
	      <>
		{sectionProps && (
		    <>
		      <AccountSection {...sectionProps}/>
		      <ThemeSection {...sectionProps}/>
		      <KeyboardSection/>
		    </>)}
	      </>)}
	</div>

	<div className="hidden md:flex md:justify-center md:px-4">
	  <div className="grid w-full max-w-[58rem] grid-cols-[12rem_minmax(0,1fr)] gap-6">
	    <div
		role="tablist"
		aria-label="設定区分"
		aria-orientation="vertical"
		className="sticky top-4 flex h-fit flex-col gap-2">
	      {tabs.map (tab => (
		  <button
		      key={tab.id}
		      id={tabButtonId (tab.id)}
		      type="button"
		      role="tab"
		      aria-selected={activeTab === tab.id}
		      aria-controls={tabPanelId (tab.id)}
		      tabIndex={activeTab === tab.id ? 0 : -1}
		      className={cn (
			  'rounded-xl border px-4 py-3 text-left text-sm font-medium',
			  activeTab === tab.id
			  ? ['border-slate-900 bg-slate-900 text-white',
			     'dark:border-slate-100 dark:bg-slate-100 dark:text-slate-900']
			  : ['border-slate-300 bg-white text-slate-700',
			     'dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100'],
		      )}
		      onClick={() => setActiveTab (tab.id)}
		      onKeyDown={handleTabKeyDown}>
		    {tab.label}
		    {settingsTabErrors[tab.id] ? ' ⚠' : ''}
		  </button>))}
	    </div>

	    <div className="w-full max-w-xl justify-self-center space-y-4">
	      <PageTitle>設定</PageTitle>
	      <FieldError messages={settingsError ? [settingsError] : []}/>
	      {loading ? 'Loading...' : (
		  <div
		      id={tabPanelId (activeTab)}
		      role="tabpanel"
		      aria-labelledby={tabButtonId (activeTab)}>
		    {sectionProps && (
			<>
			  {activeTab === 'account' && <AccountSection {...sectionProps}/>}
			  {activeTab === 'theme' && <ThemeSection {...sectionProps}/>}
			  {activeTab === 'keyboard' && <KeyboardSection/>}
			</>)}
		  </div>)}
	    </div>
	  </div>
	</div>

	<UserCodeDialogue
	    visible={userCodeVsbl}
	    onVisibleChange={setUserCodeVsbl}
	    user={user}
	    setUser={setUser}/>

	<InheritDialogue
	    visible={inheritVsbl}
	    onVisibleChange={setInheritVsbl}
	    setUser={setUser}/>
      </MainArea>)
}

export default SettingPage
