import { useState, useEffect, useRef, useCallback } from 'react'
import { useParams, Link, useNavigate, useSearchParams } from 'react-router-dom'
import { useRepo, useUpdateRepo, useDeleteRepo, useUploadRepoAvatar } from '../api/queries/repos'
import { useRepoMembers, useAddMember, useUpdateMember, useRemoveMember } from '../api/queries/members'
import { useDeployKeys, useCreateDeployKey, useDeleteDeployKey, useAccessTokens, useCreateAccessToken, useDeleteAccessToken } from '../api/queries/keys'
import {
  useBranchProtections, useCreateBranchProtection, useUpdateBranchProtection, useDeleteBranchProtection,
  useBranchingModel, useUpdateBranchingModel,
  useMergeStrategies, useUpdateMergeStrategies,
  useWebhooks, useCreateWebhook, useUpdateWebhook, useDeleteWebhook, useTestWebhook,
} from '../api/queries/workflow'
import {
  useDefaultReviewers, useAddDefaultReviewer, useRemoveDefaultReviewer,
  useDefaultDescription, useUpdateDefaultDescription,
  useExcludedFiles, useUpdateExcludedFiles,
} from '../api/queries/prs'
import { useLFSSettings, useUpdateLFSSettings } from '../api/queries/lfs'
import { useRecentRepos } from '../hooks/useRecentRepos'
import { useAuth } from '../contexts/AuthContext'
import { Skeleton } from '../ui/Skeleton'
import { RepoAvatar } from '../ui/RepoAvatar'

// ─── Sidebar config ───────────────────────────────────────────────────────────

type SectionId =
  | 'repository-details'
  | 'repository-permissions'
  | 'access-keys'
  | 'access-tokens'
  | 'branch-restrictions'
  | 'branching-model'
  | 'merge-strategies'
  | 'webhooks'
  | 'default-reviewers'
  | 'default-description'
  | 'excluded-files'
  | 'git-lfs'

const SIDEBAR: { group: string; items: { id: SectionId; label: string }[] }[] = [
  {
    group: 'General',
    items: [
      { id: 'repository-details', label: 'Repository details' },
      { id: 'repository-permissions', label: 'Repository permissions' },
    ],
  },
  {
    group: 'Security',
    items: [
      { id: 'access-keys', label: 'Access keys' },
      { id: 'access-tokens', label: 'Access tokens' },
    ],
  },
  {
    group: 'Workflow',
    items: [
      { id: 'branch-restrictions', label: 'Branch restrictions' },
      { id: 'branching-model', label: 'Branching model' },
      { id: 'merge-strategies', label: 'Merge strategies' },
      { id: 'webhooks', label: 'Webhooks' },
    ],
  },
  {
    group: 'Pull Requests',
    items: [
      { id: 'default-reviewers', label: 'Default reviewers' },
      { id: 'default-description', label: 'Default description' },
      { id: 'excluded-files', label: 'Excluded files' },
    ],
  },
  {
    group: 'Features',
    items: [
      { id: 'git-lfs', label: 'Git LFS' },
    ],
  },
]

const SECTION_META: Record<SectionId, { title: string; description: string }> = {
  'repository-details': { title: 'Repository details', description: '' },
  'repository-permissions': { title: 'Repository permissions', description: 'Control who can read, write, or administer this repository. Invite collaborators and manage team access.' },
  'access-keys': { title: 'Access keys', description: 'SSH deploy keys give read or write access to this repository without requiring an interactive user account. Useful for CI/CD.' },
  'access-tokens': { title: 'Access tokens', description: 'Repository access tokens give third-party tools secure, scoped access to this repository via the API.' },
  'branch-restrictions': { title: 'Branch restrictions', description: 'Protect important branches by restricting who can push, delete, or force-push to them.' },
  'branching-model': { title: 'Branching model', description: 'Define a branching strategy (e.g. Gitflow) to enforce consistent branch naming across your team.' },
  'merge-strategies': { title: 'Merge strategies', description: 'Configure which merge strategies (merge commit, squash, rebase) are allowed for pull requests.' },
  'webhooks': { title: 'Webhooks', description: 'Send real-time HTTP POST notifications to external services when events occur in this repository.' },
  'default-reviewers': { title: 'Default reviewers', description: 'Automatically add reviewers to pull requests based on changed files or the target branch.' },
  'default-description': { title: 'Default description', description: 'Set a template that pre-fills the pull request body when a new PR is created.' },
  'excluded-files': { title: 'Excluded files', description: 'Specify files that should be excluded from pull request diff views and review requirements.' },
  'git-lfs': { title: 'Git LFS', description: 'Git Large File Storage replaces large files with text pointers inside git, while storing the actual files on a remote server.' },
}

// ─── Page shell ───────────────────────────────────────────────────────────────

export default function RepoSettingsPage() {
  const { owner = '', repo: repoName = '' } = useParams<{ owner: string; repo: string }>()
  const [searchParams, setSearchParams] = useSearchParams()
  const [sidebarSearch, setSidebarSearch] = useState('')
  const section = (searchParams.get('section') ?? 'repository-details') as SectionId

  const filtered = sidebarSearch.trim()
    ? SIDEBAR.map(g => ({ ...g, items: g.items.filter(i => i.label.toLowerCase().includes(sidebarSearch.toLowerCase())) })).filter(g => g.items.length > 0)
    : SIDEBAR

  return (
    <div className="flex h-full overflow-hidden">
      {/* Sidebar */}
      <aside className="w-60 shrink-0 border-r border-[var(--c-border)] bg-[var(--c-surface)] hidden md:flex flex-col">
        <div className="p-3 border-b border-[var(--c-border)] space-y-2.5 shrink-0">
          <Link to={`/repos/${owner}/${repoName}`} className="flex items-center gap-1.5 text-sm text-[var(--c-text)] hover:text-[var(--c-brand)] font-medium">
            <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
            </svg>
            Repository settings
          </Link>
          <div className="relative">
            <svg className="absolute left-2.5 top-1/2 -translate-y-1/2 text-[var(--c-muted)] pointer-events-none" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
            </svg>
            <input value={sidebarSearch} onChange={e => setSidebarSearch(e.target.value)} placeholder="Jump to settings…" className="w-full text-xs bg-[var(--c-surface-muted)] border border-[var(--c-border)] rounded pl-7 pr-3 py-1.5 focus:outline-none focus:border-[var(--c-brand-focus)]" />
          </div>
        </div>
        <nav className="flex-1 overflow-y-auto py-2">
          {filtered.map(group => (
            <div key={group.group} className="mb-1">
              <p className="text-[10px] font-semibold text-[var(--c-muted)] uppercase tracking-wider px-4 py-1.5">{group.group}</p>
              {group.items.map(item => (
                <button key={item.id} onClick={() => setSearchParams({ section: item.id })}
                  className={`w-full text-left px-4 py-2 text-sm transition-colors border-l-[3px] ${section === item.id ? 'bg-[var(--c-brand-tint)] text-[var(--c-brand)] font-medium border-[var(--c-brand)]' : 'text-[var(--c-text)] hover:bg-[var(--c-surface-muted)] border-transparent'}`}>
                  {item.label}
                </button>
              ))}
            </div>
          ))}
        </nav>
      </aside>

      {/* Content */}
      <main className="flex-1 overflow-y-auto min-w-0">
        <div className="md:hidden p-3 border-b border-[var(--c-border)] bg-[var(--c-surface)]">
          <select value={section} onChange={e => setSearchParams({ section: e.target.value })} className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm focus:outline-none">
            {SIDEBAR.map(g => g.items.map(i => <option key={i.id} value={i.id}>{g.group} → {i.label}</option>))}
          </select>
        </div>
        {section === 'repository-details' && <RepositoryDetailsSection owner={owner} repo={repoName} />}
        {section === 'repository-permissions' && <RepositoryPermissionsSection owner={owner} repo={repoName} />}
        {section === 'access-keys' && <AccessKeysSection owner={owner} repo={repoName} />}
        {section === 'access-tokens' && <AccessTokensSection owner={owner} repo={repoName} />}
        {section === 'branch-restrictions' && <BranchRestrictionsSection owner={owner} repo={repoName} />}
        {section === 'branching-model' && <BranchingModelSection owner={owner} repo={repoName} />}
        {section === 'merge-strategies' && <MergeStrategiesSection owner={owner} repo={repoName} />}
        {section === 'webhooks' && <WebhooksSection owner={owner} repo={repoName} />}
        {section === 'default-reviewers' && <DefaultReviewersSection owner={owner} repo={repoName} />}
        {section === 'default-description' && <DefaultDescriptionSection owner={owner} repo={repoName} />}
        {section === 'excluded-files' && <ExcludedFilesSection owner={owner} repo={repoName} />}
        {section === 'git-lfs' && <GitLFSSection owner={owner} repo={repoName} />}
        {!['repository-details','repository-permissions','access-keys','access-tokens',
            'branch-restrictions','branching-model','merge-strategies','webhooks',
            'default-reviewers','default-description','excluded-files','git-lfs'].includes(section) && <ComingSoon sectionId={section} />}
      </main>
    </div>
  )
}

// ─── Repository details ───────────────────────────────────────────────────────

function formatSize(bytes: number): string {
  if (!bytes) return '—'
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`
  if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`
}

function RepositoryDetailsSection({ owner, repo }: { owner: string; repo: string }) {
  const navigate = useNavigate()
  const { data: repoData, isLoading } = useRepo(owner, repo)
  const updateRepo = useUpdateRepo(owner, repo)
  const deleteRepo = useDeleteRepo(owner, repo)
  const uploadAvatar = useUploadRepoAvatar(owner, repo)
  const { remove: removeRecent } = useRecentRepos()

  // Form state
  const [name, setName] = useState('')
  const [description, setDescription] = useState('')
  const [isPrivate, setIsPrivate] = useState(false)
  const [defaultBranch, setDefaultBranch] = useState('')
  const [showAdvanced, setShowAdvanced] = useState(false)
  const [showManage, setShowManage] = useState(false)
  const [confirmDelete, setConfirmDelete] = useState('')
  const [saved, setSaved] = useState(false)
  const [nameError, setNameError] = useState('')

  // Avatar state
  const fileInputRef = useRef<HTMLInputElement>(null)
  const [avatarPreview, setAvatarPreview] = useState<string | null>(null)
  const manageRef = useRef<HTMLDivElement>(null)

  // Initialize form from API data
  useEffect(() => {
    if (repoData) {
      setName(repoData.name)
      setDescription(repoData.description ?? '')
      setIsPrivate(repoData.isPrivate)
      setDefaultBranch(repoData.defaultBranch)
    }
  }, [repoData])

  // Close manage dropdown on outside click
  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (manageRef.current && !manageRef.current.contains(e.target as Node)) setShowManage(false)
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [])

  const isDirty = repoData != null && (
    name !== repoData.name ||
    description !== (repoData.description ?? '') ||
    isPrivate !== repoData.isPrivate ||
    defaultBranch !== repoData.defaultBranch
  )

  function handleDiscard() {
    if (!repoData) return
    setName(repoData.name)
    setDescription(repoData.description ?? '')
    setIsPrivate(repoData.isPrivate)
    setDefaultBranch(repoData.defaultBranch)
    setNameError('')
  }

  function handleNameChange(val: string) {
    setName(val)
    if (val && !/^[a-zA-Z0-9._-]+$/.test(val)) {
      setNameError('Only letters, numbers, hyphens, underscores, and dots are allowed.')
    } else {
      setNameError('')
    }
  }

  async function handleSave(e: React.FormEvent) {
    e.preventDefault()
    if (nameError) return
    const payload: Record<string, unknown> = { description, isPrivate, defaultBranch }
    if (name !== repoData?.name) payload.name = name
    const updated = await updateRepo.mutateAsync(payload)
    if (updated.name !== repo) {
      // Renamed — navigate to new URL
      navigate(`/repos/${owner}/${updated.name}/settings?section=repository-details`, { replace: true })
    } else {
      setSaved(true)
      setTimeout(() => setSaved(false), 3000)
    }
  }

  async function handleDelete() {
    if (confirmDelete !== repo) return
    await deleteRepo.mutateAsync()
    removeRecent(owner, repo)
    navigate('/repos')
  }

  const handleAvatarFile = useCallback((file: File) => {
    const reader = new FileReader()
    reader.onload = () => setAvatarPreview(reader.result as string)
    reader.readAsDataURL(file)
    uploadAvatar.mutate(file)
  }, [uploadAvatar])

  if (isLoading || !repoData) {
    return (
      <div className="max-w-2xl px-6 py-6 space-y-4">
        <Skeleton className="h-8 w-48" />
        <Skeleton className="h-5 w-full" />
        <Skeleton className="h-24 w-full" />
      </div>
    )
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">

      {/* Page header */}
      <div className="flex items-start justify-between gap-4 flex-wrap">
        <div>
          <div className="flex items-center gap-1 text-xs text-[var(--c-muted)] mb-1.5">
            <Link to={`/repos/${owner}/${repo}`} className="hover:text-[var(--c-brand)]">{owner}</Link>
            <span>/</span>
            <Link to={`/repos/${owner}/${repo}`} className="hover:text-[var(--c-brand)]">{repo}</Link>
          </div>
          <h1 className="text-xl font-semibold text-[var(--c-text)]">Repository details</h1>
        </div>

        {/* Manage repository dropdown */}
        <div className="relative shrink-0" ref={manageRef}>
          <button
            onClick={() => setShowManage(s => !s)}
            className="flex items-center gap-1.5 px-3 py-1.5 border border-[var(--c-border)] rounded text-sm text-[var(--c-text)] hover:bg-[var(--c-surface-muted)] font-medium"
          >
            Manage repository
            <svg width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
            </svg>
          </button>
          {showManage && (
            <div className="absolute right-0 top-full mt-1 w-52 bg-[var(--c-surface)] border border-[var(--c-border)] rounded-lg shadow-xl z-20 overflow-hidden">
              <button
                onClick={() => { setShowManage(false); setShowAdvanced(true); setTimeout(() => document.getElementById('danger-zone')?.scrollIntoView({ behavior: 'smooth' }), 50) }}
                className="w-full flex items-center gap-2 px-4 py-3 text-sm text-[var(--c-danger)] hover:bg-[var(--c-danger-tint)] text-left"
              >
                <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.5" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
                </svg>
                Delete repository
              </button>
            </div>
          )}
        </div>
      </div>

      <div className="border-t border-[var(--c-border)]" />

      <form onSubmit={handleSave} className="space-y-6">

        {/* Avatar */}
        <div>
          <label className="block text-sm font-medium text-[var(--c-text)] mb-3">Avatar</label>
          <div className="flex items-center gap-4">
            {/* Clickable avatar */}
            <button
              type="button"
              onClick={() => fileInputRef.current?.click()}
              className="rounded-lg overflow-hidden ring-2 ring-[var(--c-border)] hover:ring-[var(--c-brand-focus)] transition-all focus:outline-none"
              title="Click to change avatar"
            >
              {avatarPreview ? (
                <img src={avatarPreview} alt="Preview" className="w-14 h-14 object-cover" />
              ) : (
                <RepoAvatar
                  ownerName={owner}
                  name={repoData.name}
                  avatarUrl={repoData.avatarUrl}
                  size={56}
                />
              )}
            </button>
            <div>
              <button
                type="button"
                onClick={() => fileInputRef.current?.click()}
                disabled={uploadAvatar.isPending}
                className="px-3 py-1.5 text-sm border border-[var(--c-border)] rounded text-[var(--c-text)] hover:bg-[var(--c-surface-muted)] disabled:opacity-50"
              >
                {uploadAvatar.isPending ? 'Uploading…' : 'Change avatar'}
              </button>
              <p className="text-xs text-[var(--c-muted)] mt-1">JPEG, PNG, GIF or WebP · max 5 MB</p>
              {uploadAvatar.isError && (
                <p className="text-xs text-[var(--c-danger)] mt-1">{(uploadAvatar.error as Error).message}</p>
              )}
              {uploadAvatar.isSuccess && !avatarPreview && (
                <p className="text-xs text-[var(--c-success)] mt-1">Avatar updated.</p>
              )}
            </div>
            <input
              ref={fileInputRef}
              type="file"
              accept="image/jpeg,image/png,image/gif,image/webp"
              className="hidden"
              onChange={e => { const f = e.target.files?.[0]; if (f) handleAvatarFile(f) }}
            />
          </div>
        </div>

        {/* Repository name */}
        <div>
          <label className="block text-sm font-medium text-[var(--c-text)] mb-1">
            Repository name <span className="text-[var(--c-danger)]">*</span>
          </label>
          <input
            value={name}
            onChange={e => handleNameChange(e.target.value)}
            className={`w-full border rounded px-3 py-2 text-sm focus:outline-none focus:ring-1 ${nameError ? 'border-[var(--c-danger)] focus:border-[var(--c-danger)] focus:ring-[var(--c-danger)]' : 'border-[var(--c-border)] focus:border-[var(--c-brand-focus)] focus:ring-[var(--c-brand-focus)]'}`}
          />
          {nameError
            ? <p className="text-xs text-[var(--c-danger)] mt-1">{nameError}</p>
            : name !== repoData.name
              ? <p className="text-xs text-[var(--c-warning)] mt-1 flex items-center gap-1">
                  <svg width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" /></svg>
                  Renaming will change the clone URL — all existing git remotes will need to be updated.
                </p>
              : <p className="text-xs text-[var(--c-muted)] mt-1">Letters, numbers, hyphens, underscores, and dots only.</p>
          }
        </div>

        {/* Size (read-only) */}
        <div>
          <label className="block text-sm font-medium text-[var(--c-text)] mb-1">Size</label>
          <p className="text-sm text-[var(--c-text)]">{formatSize(repoData.size)}</p>
        </div>

        {/* Description */}
        <div>
          <label className="block text-sm font-medium text-[var(--c-text)] mb-1">Description</label>
          <textarea
            value={description}
            onChange={e => setDescription(e.target.value)}
            rows={4}
            placeholder="Describe this repository…"
            className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--c-brand-focus)] focus:ring-1 focus:ring-[var(--c-brand-focus)] resize-y"
          />
        </div>

        {/* Access level */}
        <div>
          <label className="block text-sm font-medium text-[var(--c-text)] mb-2">Access level</label>
          <label className="flex items-start gap-2.5 cursor-pointer">
            <input
              type="checkbox"
              checked={isPrivate}
              onChange={e => setIsPrivate(e.target.checked)}
              className="mt-0.5 w-4 h-4 accent-[var(--c-brand)]"
            />
            <div>
              <span className="text-sm font-medium text-[var(--c-text)]">
                {isPrivate ? 'This is a private repository' : 'This is a public repository'}
              </span>
              <p className="text-xs text-[var(--c-muted)] mt-0.5">
                {isPrivate
                  ? 'Only collaborators you invite can see and push to this repository.'
                  : 'Anyone can view this repository. Make it private to restrict access.'}
              </p>
            </div>
          </label>
        </div>

        {/* Advanced (collapsible) */}
        <div className="border border-[var(--c-border)] rounded-lg overflow-hidden">
          <button
            type="button"
            onClick={() => setShowAdvanced(s => !s)}
            className="w-full flex items-center gap-2 px-4 py-3 text-sm font-medium text-[var(--c-text)] hover:bg-[var(--c-surface-muted)] text-left"
          >
            <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24"
              className={`transition-transform shrink-0 ${showAdvanced ? 'rotate-90' : ''}`}>
              <path strokeLinecap="round" strokeLinejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
            </svg>
            Advanced
          </button>

          {showAdvanced && (
            <div className="border-t border-[var(--c-border)] px-4 py-5 space-y-6 bg-[var(--c-surface-raised)]">
              {/* Default branch */}
              <div>
                <label className="block text-sm font-medium text-[var(--c-text)] mb-1">Default branch</label>
                <input
                  value={defaultBranch}
                  onChange={e => setDefaultBranch(e.target.value)}
                  placeholder="main"
                  className="w-full max-w-xs bg-[var(--c-surface)] border border-[var(--c-border)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--c-brand-focus)]"
                />
                <p className="text-xs text-[var(--c-muted)] mt-1">The default branch used as the base for new pull requests.</p>
              </div>

              {/* Danger zone */}
              <div id="danger-zone" className="border border-[var(--c-danger-tint)] rounded-lg overflow-hidden">
                <div className="px-4 py-3 bg-[var(--c-danger-tint)]/60 border-b border-[var(--c-danger-tint)]">
                  <h3 className="text-sm font-semibold text-[var(--c-danger-dark)]">Delete repository</h3>
                </div>
                <div className="px-4 py-4 space-y-3 bg-[var(--c-surface)]">
                  <p className="text-sm text-[var(--c-text)]">
                    This is permanent and cannot be undone. All commits, branches, pull requests, issues, and settings will be permanently deleted.
                  </p>
                  <p className="text-xs text-[var(--c-muted)]">
                    Type <code className="font-mono bg-[var(--c-surface-muted)] border border-[var(--c-border)] px-1.5 py-0.5 rounded text-[var(--c-text)]">{repo}</code> to confirm.
                  </p>
                  <input
                    value={confirmDelete}
                    onChange={e => setConfirmDelete(e.target.value)}
                    placeholder={repo}
                    className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm focus:outline-none focus:border-[var(--c-danger)]"
                  />
                  <button
                    type="button"
                    onClick={handleDelete}
                    disabled={confirmDelete !== repo || deleteRepo.isPending}
                    className="px-4 py-2 rounded bg-[var(--c-danger)] text-white text-sm font-medium hover:bg-[var(--c-danger-dark)] disabled:opacity-40"
                  >
                    {deleteRepo.isPending ? 'Deleting…' : 'Delete repository'}
                  </button>
                  {deleteRepo.isError && (
                    <p className="text-xs text-[var(--c-danger)]">{(deleteRepo.error as Error).message}</p>
                  )}
                </div>
              </div>
            </div>
          )}
        </div>

        {/* Footer: error / saved / Discard / Save */}
        <div className="flex items-center justify-end gap-3 pt-2 border-t border-[var(--c-border)]">
          {updateRepo.isError && (
            <p className="text-xs text-[var(--c-danger)] mr-auto">{(updateRepo.error as Error).message}</p>
          )}
          {saved && !updateRepo.isError && (
            <span className="text-xs text-[var(--c-success)] font-medium mr-auto flex items-center gap-1">
              <svg width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" d="m4.5 12.75 6 6 9-13.5" />
              </svg>
              Changes saved
            </span>
          )}
          <button type="button" onClick={handleDiscard} disabled={!isDirty}
            className="px-4 py-2 rounded border border-[var(--c-border)] text-sm text-[var(--c-text)] hover:bg-[var(--c-surface-muted)] disabled:opacity-40 min-h-[36px]">
            Discard
          </button>
          <button type="submit" disabled={updateRepo.isPending || !isDirty || !!nameError}
            className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50 min-h-[36px]">
            {updateRepo.isPending ? 'Saving…' : 'Save changes'}
          </button>
        </div>
      </form>
    </div>
  )
}

// ─── Repository permissions section ──────────────────────────────────────────

const PERMISSION_LABELS: Record<string, { label: string; description: string; color: string }> = {
  read:  { label: 'Read',  description: 'Can clone and pull',             color: 'bg-[var(--c-surface-muted)] text-[var(--c-muted)]' },
  write: { label: 'Write', description: 'Can push branches and commits',  color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300' },
  admin: { label: 'Admin', description: 'Can manage settings and members', color: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300' },
}

function PermissionBadge({ permission }: { permission: string }) {
  const p = PERMISSION_LABELS[permission] ?? PERMISSION_LABELS.read
  return (
    <span className={`text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-full ${p.color}`}>
      {p.label}
    </span>
  )
}

function RepositoryPermissionsSection({ owner, repo }: { owner: string; repo: string }) {
  const { user } = useAuth()
  const { data: members, isLoading } = useRepoMembers(owner, repo)
  const addMember = useAddMember(owner, repo)
  const updateMember = useUpdateMember(owner, repo)
  const removeMember = useRemoveMember(owner, repo)

  const [username, setUsername] = useState('')
  const [permission, setPermission] = useState('write')
  const [addError, setAddError] = useState('')

  const isOwner = members?.find(m => m.isOwner)?.username === user?.username

  async function handleAdd(e: React.FormEvent) {
    e.preventDefault()
    setAddError('')
    if (!username.trim()) return
    try {
      await addMember.mutateAsync({ username: username.trim(), permission })
      setUsername('')
    } catch (err) {
      setAddError((err as Error).message)
    }
  }

  async function handlePermissionChange(memberUsername: string, newPermission: string) {
    await updateMember.mutateAsync({ username: memberUsername, permission: newPermission })
  }

  async function handleRemove(memberUsername: string) {
    await removeMember.mutateAsync(memberUsername)
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Repository permissions</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">
          Manage who has access to this repository and what they can do.
        </p>
      </div>

      <div className="border-t border-[var(--c-border)]" />

      {/* Permission level reference */}
      <div className="grid grid-cols-3 gap-3">
        {Object.entries(PERMISSION_LABELS).map(([key, val]) => (
          <div key={key} className="border border-[var(--c-border)] rounded-lg p-3 bg-[var(--c-surface)]">
            <PermissionBadge permission={key} />
            <p className="text-xs text-[var(--c-muted)] mt-2">{val.description}</p>
          </div>
        ))}
      </div>

      {/* Member list */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)] flex items-center justify-between">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Members</h2>
          <span className="text-xs text-[var(--c-muted)]">{members?.length ?? 0} {members?.length === 1 ? 'person' : 'people'}</span>
        </div>

        {isLoading ? (
          <div className="p-4 space-y-3">
            {[1,2].map(i => (
              <div key={i} className="flex items-center gap-3">
                <Skeleton className="w-8 h-8 rounded-full" />
                <Skeleton className="h-4 w-32" />
                <Skeleton className="h-4 w-16 ml-auto" />
              </div>
            ))}
          </div>
        ) : (
          <ul className="divide-y divide-[var(--c-border)]">
            {members?.map(member => (
              <li key={member.userId} className="flex items-center gap-3 px-4 py-3">
                {/* Avatar */}
                <div className="w-8 h-8 rounded-full overflow-hidden shrink-0 bg-[var(--c-brand)] flex items-center justify-center text-white text-sm font-bold">
                  {member.avatarUrl
                    ? <img src={member.avatarUrl} alt={member.username} className="w-full h-full object-cover" />
                    : member.username[0]?.toUpperCase()
                  }
                </div>

                {/* Name */}
                <div className="flex-1 min-w-0">
                  <span className="text-sm font-medium text-[var(--c-text)]">{member.username}</span>
                  {member.isOwner && (
                    <span className="ml-2 text-[10px] text-[var(--c-muted)]">owner</span>
                  )}
                </div>

                {/* Permission selector or badge */}
                {member.isOwner ? (
                  <PermissionBadge permission="admin" />
                ) : isOwner ? (
                  <div className="flex items-center gap-2">
                    <select
                      value={member.permission}
                      onChange={e => handlePermissionChange(member.username, e.target.value)}
                      disabled={updateMember.isPending}
                      className="text-xs border border-[var(--c-border)] rounded px-2 py-1 bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]"
                    >
                      <option value="read">Read</option>
                      <option value="write">Write</option>
                      <option value="admin">Admin</option>
                    </select>
                    <button
                      onClick={() => handleRemove(member.username)}
                      disabled={removeMember.isPending}
                      className="text-[var(--c-danger)] hover:text-[var(--c-danger-dark)] disabled:opacity-40 p-1 rounded hover:bg-[var(--c-danger-tint)] transition-colors"
                      title="Remove member"
                    >
                      <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
                      </svg>
                    </button>
                  </div>
                ) : (
                  <PermissionBadge permission={member.permission} />
                )}
              </li>
            ))}
          </ul>
        )}
      </div>

      {/* Add member form — only for owner/admin */}
      {isOwner && (
        <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
          <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)]">
            <h2 className="text-sm font-semibold text-[var(--c-text)]">Add a member</h2>
          </div>
          <form onSubmit={handleAdd} className="p-4 space-y-4">
            <div className="flex gap-3 flex-wrap">
              <div className="flex-1 min-w-[180px]">
                <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Username</label>
                <input
                  value={username}
                  onChange={e => { setUsername(e.target.value); setAddError('') }}
                  placeholder="e.g. alice"
                  className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)] focus:ring-1 focus:ring-[var(--c-brand-focus)]"
                />
              </div>
              <div className="w-36">
                <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Permission</label>
                <select
                  value={permission}
                  onChange={e => setPermission(e.target.value)}
                  className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]"
                >
                  <option value="read">Read</option>
                  <option value="write">Write</option>
                  <option value="admin">Admin</option>
                </select>
              </div>
            </div>

            <div className="text-xs text-[var(--c-muted)]">
              {PERMISSION_LABELS[permission]?.description}
            </div>

            {addError && (
              <p className="text-xs text-[var(--c-danger)]">{addError}</p>
            )}

            <button
              type="submit"
              disabled={addMember.isPending || !username.trim()}
              className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50"
            >
              {addMember.isPending ? 'Adding…' : 'Add member'}
            </button>
          </form>
        </div>
      )}

      {/* Info for non-owners */}
      {!isOwner && !isLoading && (
        <div className="flex items-start gap-3 p-4 border border-[var(--c-border)] rounded-lg bg-[var(--c-surface-raised)]">
          <svg width="16" height="16" fill="none" stroke="var(--c-muted)" strokeWidth="1.5" viewBox="0 0 24 24" className="shrink-0 mt-0.5">
            <path strokeLinecap="round" strokeLinejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
          </svg>
          <p className="text-sm text-[var(--c-muted)]">
            Only the repository owner and admins can manage member permissions.
          </p>
        </div>
      )}
    </div>
  )
}

// ─── Shared helpers ───────────────────────────────────────────────────────────

function TokenReveal({ token, onDismiss }: { token: string; onDismiss: () => void }) {
  const [copied, setCopied] = useState(false)
  function copy() {
    navigator.clipboard.writeText(token)
    setCopied(true)
    setTimeout(() => setCopied(false), 2000)
  }
  return (
    <div className="border border-[var(--c-success)] rounded-lg overflow-hidden">
      <div className="flex items-center gap-2 px-4 py-2.5 bg-[var(--c-surface-raised)] border-b border-[var(--c-success)]/30">
        <svg width="14" height="14" fill="none" stroke="var(--c-success)" strokeWidth="2" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
        </svg>
        <span className="text-sm font-semibold text-[var(--c-success)]">Token created — copy it now, it won't be shown again</span>
      </div>
      <div className="p-4 space-y-3 bg-[var(--c-surface)]">
        <div className="flex items-center gap-2">
          <code className="flex-1 font-mono text-xs bg-[var(--c-surface-muted)] border border-[var(--c-border)] rounded px-3 py-2 text-[var(--c-text)] break-all">
            {token}
          </code>
          <button onClick={copy}
            className="shrink-0 px-3 py-2 rounded border border-[var(--c-border)] text-xs font-medium text-[var(--c-text)] hover:bg-[var(--c-surface-muted)] transition-colors">
            {copied ? 'Copied!' : 'Copy'}
          </button>
        </div>
        <button onClick={onDismiss} className="text-xs text-[var(--c-muted)] hover:text-[var(--c-text)] underline">
          I've copied it, dismiss
        </button>
      </div>
    </div>
  )
}

// ─── Access keys (deploy keys) section ───────────────────────────────────────

function AccessKeysSection({ owner, repo }: { owner: string; repo: string }) {
  const { data: keys, isLoading } = useDeployKeys(owner, repo)
  const createKey = useCreateDeployKey(owner, repo)
  const deleteKey = useDeleteDeployKey(owner, repo)

  const [title, setTitle] = useState('')
  const [readOnly, setReadOnly] = useState(true)
  const [newToken, setNewToken] = useState<string | null>(null)
  const [formError, setFormError] = useState('')

  async function handleCreate(e: React.FormEvent) {
    e.preventDefault()
    setFormError('')
    if (!title.trim()) return
    try {
      const key = await createKey.mutateAsync({ title: title.trim(), readOnly })
      if (key.token) setNewToken(key.token)
      setTitle('')
      setReadOnly(true)
    } catch (err) {
      setFormError((err as Error).message)
    }
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Access keys</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">
          Deploy keys grant git clone and push access to this repository over HTTP. Each key is shown only once.
        </p>
        <div className="mt-3 p-3 rounded-lg bg-[var(--c-surface-raised)] border border-[var(--c-border)] text-xs text-[var(--c-muted)]">
          Use as git credentials: <code className="font-mono bg-[var(--c-surface-muted)] px-1 rounded">git clone http://x-deploy-key:&lt;TOKEN&gt;@{window.location.hostname}/{owner}/{repo}.git</code>
        </div>
      </div>

      <div className="border-t border-[var(--c-border)]" />

      {/* Token reveal banner */}
      {newToken && <TokenReveal token={newToken} onDismiss={() => setNewToken(null)} />}

      {/* Existing keys */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)] flex items-center justify-between">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Active keys</h2>
          <span className="text-xs text-[var(--c-muted)]">{keys?.length ?? 0} key{keys?.length !== 1 ? 's' : ''}</span>
        </div>

        {isLoading ? (
          <div className="p-4 space-y-3">
            {[1, 2].map(i => <div key={i} className="flex gap-3"><Skeleton className="h-4 w-32" /><Skeleton className="h-4 w-16 ml-auto" /></div>)}
          </div>
        ) : !keys?.length ? (
          <div className="px-4 py-8 text-center text-sm text-[var(--c-muted)]">No deploy keys yet.</div>
        ) : (
          <ul className="divide-y divide-[var(--c-border)]">
            {keys.map(key => (
              <li key={key.id} className="flex items-center gap-3 px-4 py-3">
                <svg width="16" height="16" fill="none" stroke="var(--c-muted)" strokeWidth="1.5" viewBox="0 0 24 24" className="shrink-0">
                  <path strokeLinecap="round" strokeLinejoin="round" d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 0 1 21.75 8.25Z" />
                </svg>
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-medium text-[var(--c-text)]">{key.title}</p>
                  <p className="text-xs text-[var(--c-muted)] mt-0.5">
                    {key.readOnly ? 'Read-only' : 'Read & write'} · Added {new Date(key.createdAt).toLocaleDateString()}
                  </p>
                </div>
                <span className={`text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-full ${key.readOnly ? 'bg-[var(--c-surface-muted)] text-[var(--c-muted)]' : 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300'}`}>
                  {key.readOnly ? 'Read' : 'Write'}
                </span>
                <button
                  onClick={() => deleteKey.mutate(key.id)}
                  disabled={deleteKey.isPending}
                  className="text-[var(--c-danger)] hover:text-[var(--c-danger-dark)] p-1 rounded hover:bg-[var(--c-danger-tint)] transition-colors disabled:opacity-40"
                  title="Revoke key"
                >
                  <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
                  </svg>
                </button>
              </li>
            ))}
          </ul>
        )}
      </div>

      {/* Create form */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)]">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Add a deploy key</h2>
        </div>
        <form onSubmit={handleCreate} className="p-4 space-y-4">
          <div>
            <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Label</label>
            <input value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. CI/CD pipeline"
              className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)] focus:ring-1 focus:ring-[var(--c-brand-focus)]" />
          </div>
          <label className="flex items-start gap-2.5 cursor-pointer">
            <input type="checkbox" checked={readOnly} onChange={e => setReadOnly(e.target.checked)}
              className="mt-0.5 w-4 h-4 accent-[var(--c-brand)]" />
            <div>
              <span className="text-sm font-medium text-[var(--c-text)]">Read-only</span>
              <p className="text-xs text-[var(--c-muted)] mt-0.5">Uncheck to allow pushes with this key.</p>
            </div>
          </label>
          {formError && <p className="text-xs text-[var(--c-danger)]">{formError}</p>}
          <button type="submit" disabled={createKey.isPending || !title.trim()}
            className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50">
            {createKey.isPending ? 'Generating…' : 'Generate key'}
          </button>
        </form>
      </div>
    </div>
  )
}

// ─── Access tokens section ────────────────────────────────────────────────────

function AccessTokensSection({ owner, repo }: { owner: string; repo: string }) {
  const { data: tokens, isLoading } = useAccessTokens(owner, repo)
  const createToken = useCreateAccessToken(owner, repo)
  const deleteToken = useDeleteAccessToken(owner, repo)

  const [title, setTitle] = useState('')
  const [scopes, setScopes] = useState<string[]>(['read'])
  const [expiresAt, setExpiresAt] = useState('')
  const [newToken, setNewToken] = useState<string | null>(null)
  const [formError, setFormError] = useState('')

  function toggleScope(scope: string) {
    setScopes(prev =>
      prev.includes(scope) ? prev.filter(s => s !== scope) : [...prev, scope]
    )
  }

  async function handleCreate(e: React.FormEvent) {
    e.preventDefault()
    setFormError('')
    if (!title.trim() || scopes.length === 0) return
    try {
      const tok = await createToken.mutateAsync({
        title: title.trim(),
        scopes: scopes.join(','),
        expiresAt: expiresAt || undefined,
      })
      if (tok.token) setNewToken(tok.token)
      setTitle('')
      setScopes(['read'])
      setExpiresAt('')
    } catch (err) {
      setFormError((err as Error).message)
    }
  }

  const scopeLabels: Record<string, { label: string; description: string }> = {
    read:  { label: 'Read',  description: 'Read repo contents, branches, commits, PRs, issues' },
    write: { label: 'Write', description: 'Push code, create PRs and issues' },
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Access tokens</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">
          Tokens grant programmatic access to this repository's API and git operations. Each token is shown only once.
        </p>
        <div className="mt-3 p-3 rounded-lg bg-[var(--c-surface-raised)] border border-[var(--c-border)] text-xs text-[var(--c-muted)] space-y-1">
          <p>API: <code className="font-mono bg-[var(--c-surface-muted)] px-1 rounded">Authorization: Bearer &lt;TOKEN&gt;</code></p>
          <p>Git: <code className="font-mono bg-[var(--c-surface-muted)] px-1 rounded">git clone http://x-token:&lt;TOKEN&gt;@{window.location.hostname}/{owner}/{repo}.git</code></p>
        </div>
      </div>

      <div className="border-t border-[var(--c-border)]" />

      {newToken && <TokenReveal token={newToken} onDismiss={() => setNewToken(null)} />}

      {/* Token list */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)] flex items-center justify-between">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Active tokens</h2>
          <span className="text-xs text-[var(--c-muted)]">{tokens?.length ?? 0} token{tokens?.length !== 1 ? 's' : ''}</span>
        </div>

        {isLoading ? (
          <div className="p-4 space-y-3">
            {[1, 2].map(i => <div key={i} className="flex gap-3"><Skeleton className="h-4 w-40" /><Skeleton className="h-4 w-20 ml-auto" /></div>)}
          </div>
        ) : !tokens?.length ? (
          <div className="px-4 py-8 text-center text-sm text-[var(--c-muted)]">No access tokens yet.</div>
        ) : (
          <ul className="divide-y divide-[var(--c-border)]">
            {tokens.map(tok => {
              const expired = tok.expiresAt && new Date(tok.expiresAt) < new Date()
              return (
                <li key={tok.id} className="flex items-center gap-3 px-4 py-3">
                  <svg width="16" height="16" fill="none" stroke="var(--c-muted)" strokeWidth="1.5" viewBox="0 0 24 24" className="shrink-0">
                    <path strokeLinecap="round" strokeLinejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z" />
                  </svg>
                  <div className="flex-1 min-w-0">
                    <p className="text-sm font-medium text-[var(--c-text)]">{tok.title}</p>
                    <p className="text-xs text-[var(--c-muted)] mt-0.5">
                      Scopes: {tok.scopes}
                      {tok.expiresAt && (
                        <span className={expired ? ' · Expired' : ` · Expires ${new Date(tok.expiresAt).toLocaleDateString()}`} />
                      )}
                      {!tok.expiresAt && ' · No expiry'}
                    </p>
                  </div>
                  {expired && (
                    <span className="text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded-full bg-[var(--c-danger-tint)] text-[var(--c-danger)]">Expired</span>
                  )}
                  <button
                    onClick={() => deleteToken.mutate(tok.id)}
                    disabled={deleteToken.isPending}
                    className="text-[var(--c-danger)] hover:text-[var(--c-danger-dark)] p-1 rounded hover:bg-[var(--c-danger-tint)] transition-colors disabled:opacity-40"
                    title="Revoke token"
                  >
                    <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
                    </svg>
                  </button>
                </li>
              )
            })}
          </ul>
        )}
      </div>

      {/* Create form */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)]">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Create an access token</h2>
        </div>
        <form onSubmit={handleCreate} className="p-4 space-y-4">
          <div>
            <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Token name</label>
            <input value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. GitHub Actions"
              className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)] focus:ring-1 focus:ring-[var(--c-brand-focus)]" />
          </div>

          <div>
            <label className="block text-xs font-medium text-[var(--c-muted)] mb-2">Scopes</label>
            <div className="space-y-2">
              {Object.entries(scopeLabels).map(([key, val]) => (
                <label key={key} className="flex items-start gap-2.5 cursor-pointer">
                  <input type="checkbox" checked={scopes.includes(key)} onChange={() => toggleScope(key)}
                    className="mt-0.5 w-4 h-4 accent-[var(--c-brand)]" />
                  <div>
                    <span className="text-sm font-medium text-[var(--c-text)]">{val.label}</span>
                    <p className="text-xs text-[var(--c-muted)]">{val.description}</p>
                  </div>
                </label>
              ))}
            </div>
          </div>

          <div>
            <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Expiry date <span className="text-[var(--c-subtle)]">(optional)</span></label>
            <input type="date" value={expiresAt} onChange={e => setExpiresAt(e.target.value)}
              min={new Date().toISOString().split('T')[0]}
              className="border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]" />
          </div>

          {formError && <p className="text-xs text-[var(--c-danger)]">{formError}</p>}

          <button type="submit" disabled={createToken.isPending || !title.trim() || scopes.length === 0}
            className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50">
            {createToken.isPending ? 'Generating…' : 'Generate token'}
          </button>
        </form>
      </div>
    </div>
  )
}

// ─── Branch restrictions ──────────────────────────────────────────────────────

function BranchRestrictionsSection({ owner, repo }: { owner: string; repo: string }) {
  const { data: rules, isLoading } = useBranchProtections(owner, repo)
  const createRule = useCreateBranchProtection(owner, repo)
  const updateRule = useUpdateBranchProtection(owner, repo)
  const deleteRule = useDeleteBranchProtection(owner, repo)

  const [pattern, setPattern] = useState('')
  const [requirePR, setRequirePR] = useState(true)
  const [blockForcePush, setBlockForcePush] = useState(true)
  const [allowedUsers, setAllowedUsers] = useState('')
  const [error, setError] = useState('')

  async function handleCreate(e: React.FormEvent) {
    e.preventDefault()
    setError('')
    try {
      await createRule.mutateAsync({ pattern: pattern.trim(), requirePR, blockForcePush, allowedUsers: allowedUsers.trim() })
      setPattern('')
    } catch (err) { setError((err as Error).message) }
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Branch restrictions</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">
          Protect branches from direct pushes. Matched branches can only be updated via pull requests.
        </p>
      </div>
      <div className="border-t border-[var(--c-border)]" />

      {/* Rule list */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)] flex justify-between">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Protected branches</h2>
          <span className="text-xs text-[var(--c-muted)]">{rules?.length ?? 0} rule{rules?.length !== 1 ? 's' : ''}</span>
        </div>
        {isLoading ? (
          <div className="p-4"><Skeleton className="h-4 w-full" /></div>
        ) : !rules?.length ? (
          <div className="px-4 py-8 text-center text-sm text-[var(--c-muted)]">No branch protection rules yet.</div>
        ) : (
          <ul className="divide-y divide-[var(--c-border)]">
            {rules.map(rule => (
              <li key={rule.id} className="px-4 py-3 space-y-2">
                <div className="flex items-center justify-between gap-3">
                  <div className="flex items-center gap-2">
                    <svg width="14" height="14" fill="none" stroke="var(--c-brand)" strokeWidth="2" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z" />
                    </svg>
                    <code className="text-sm font-mono font-semibold text-[var(--c-text)]">{rule.pattern}</code>
                  </div>
                  <button onClick={() => deleteRule.mutate(rule.id)} disabled={deleteRule.isPending}
                    className="text-[var(--c-danger)] hover:text-[var(--c-danger-dark)] p-1 rounded hover:bg-[var(--c-danger-tint)] disabled:opacity-40">
                    <svg width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
                      <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
                    </svg>
                  </button>
                </div>
                <div className="flex gap-4 text-xs text-[var(--c-muted)] ml-6">
                  <label className="flex items-center gap-1.5 cursor-pointer">
                    <input type="checkbox" checked={rule.requirePR} className="w-3 h-3 accent-[var(--c-brand)]"
                      onChange={e => updateRule.mutate({ id: rule.id, requirePR: e.target.checked, blockForcePush: rule.blockForcePush, allowedUsers: rule.allowedUsers })} />
                    Require PR
                  </label>
                  <label className="flex items-center gap-1.5 cursor-pointer">
                    <input type="checkbox" checked={rule.blockForcePush} className="w-3 h-3 accent-[var(--c-brand)]"
                      onChange={e => updateRule.mutate({ id: rule.id, requirePR: rule.requirePR, blockForcePush: e.target.checked, allowedUsers: rule.allowedUsers })} />
                    Block force push
                  </label>
                  {rule.allowedUsers && <span>Bypass: <code className="text-[var(--c-text)]">{rule.allowedUsers}</code></span>}
                </div>
              </li>
            ))}
          </ul>
        )}
      </div>

      {/* Create rule form */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)]">
          <h2 className="text-sm font-semibold text-[var(--c-text)]">Add a rule</h2>
        </div>
        <form onSubmit={handleCreate} className="p-4 space-y-4">
          <div>
            <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Branch pattern</label>
            <input value={pattern} onChange={e => setPattern(e.target.value)} placeholder="main, release/*, v*"
              className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] font-mono focus:outline-none focus:border-[var(--c-brand-focus)] focus:ring-1 focus:ring-[var(--c-brand-focus)]" />
            <p className="text-xs text-[var(--c-muted)] mt-1">Glob patterns supported: <code className="font-mono">release/*</code> matches all release branches.</p>
          </div>
          <div className="flex gap-6">
            <label className="flex items-center gap-2 cursor-pointer text-sm text-[var(--c-text)]">
              <input type="checkbox" checked={requirePR} onChange={e => setRequirePR(e.target.checked)} className="w-4 h-4 accent-[var(--c-brand)]" />
              Require pull request
            </label>
            <label className="flex items-center gap-2 cursor-pointer text-sm text-[var(--c-text)]">
              <input type="checkbox" checked={blockForcePush} onChange={e => setBlockForcePush(e.target.checked)} className="w-4 h-4 accent-[var(--c-brand)]" />
              Block force push
            </label>
          </div>
          <div>
            <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Bypass list <span className="text-[var(--c-subtle)]">(optional — comma-separated usernames)</span></label>
            <input value={allowedUsers} onChange={e => setAllowedUsers(e.target.value)} placeholder="alice, bob"
              className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]" />
          </div>
          {error && <p className="text-xs text-[var(--c-danger)]">{error}</p>}
          <button type="submit" disabled={createRule.isPending || !pattern.trim()}
            className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50">
            {createRule.isPending ? 'Adding…' : 'Add rule'}
          </button>
        </form>
      </div>
    </div>
  )
}

// ─── Branching model ──────────────────────────────────────────────────────────

function BranchingModelSection({ owner, repo }: { owner: string; repo: string }) {
  const { data, isLoading } = useBranchingModel(owner, repo)
  const updateModel = useUpdateBranchingModel(owner, repo)
  const [form, setForm] = useState({ enabled: false, featurePrefix: 'feature/', bugfixPrefix: 'bugfix/', releasePrefix: 'release/', hotfixPrefix: 'hotfix/' })
  const [saved, setSaved] = useState(false)

  useEffect(() => { if (data) setForm(data) }, [data])

  async function handleSave(e: React.FormEvent) {
    e.preventDefault()
    await updateModel.mutateAsync(form)
    setSaved(true)
    setTimeout(() => setSaved(false), 3000)
  }

  const branches = [
    { key: 'featurePrefix' as const, label: 'Feature', example: 'feature/my-feature', color: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300' },
    { key: 'bugfixPrefix' as const, label: 'Bugfix', example: 'bugfix/fix-login', color: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300' },
    { key: 'releasePrefix' as const, label: 'Release', example: 'release/1.0.0', color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300' },
    { key: 'hotfixPrefix' as const, label: 'Hotfix', example: 'hotfix/critical-fix', color: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300' },
  ]

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Branching model</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">Define naming conventions for branch types to keep your team consistent.</p>
      </div>
      <div className="border-t border-[var(--c-border)]" />

      {isLoading ? <Skeleton className="h-48 w-full" /> : (
        <form onSubmit={handleSave} className="space-y-5">
          <label className="flex items-center gap-3 p-4 border border-[var(--c-border)] rounded-lg cursor-pointer bg-[var(--c-surface)] hover:bg-[var(--c-surface-raised)]">
            <input type="checkbox" checked={form.enabled} onChange={e => setForm(f => ({ ...f, enabled: e.target.checked }))}
              className="w-4 h-4 accent-[var(--c-brand)]" />
            <div>
              <span className="text-sm font-medium text-[var(--c-text)]">Enable branching model</span>
              <p className="text-xs text-[var(--c-muted)] mt-0.5">Apply the naming conventions below to this repository.</p>
            </div>
          </label>

          <div className={`space-y-3 transition-opacity ${form.enabled ? '' : 'opacity-40 pointer-events-none'}`}>
            {branches.map(b => (
              <div key={b.key} className="flex items-center gap-3 p-3 border border-[var(--c-border)] rounded-lg bg-[var(--c-surface)]">
                <span className={`text-[10px] font-bold uppercase tracking-wide px-2 py-1 rounded w-20 text-center shrink-0 ${b.color}`}>{b.label}</span>
                <input value={form[b.key]} onChange={e => setForm(f => ({ ...f, [b.key]: e.target.value }))}
                  className="flex-1 border border-[var(--c-border)] rounded px-3 py-1.5 text-sm font-mono bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]" />
                <span className="text-xs text-[var(--c-muted)] font-mono hidden sm:block">{form[b.key] || '…'}my-branch</span>
              </div>
            ))}
          </div>

          <div className="flex items-center gap-3 pt-2 border-t border-[var(--c-border)]">
            {saved && <span className="text-xs text-[var(--c-success)] font-medium">Saved!</span>}
            <button type="submit" disabled={updateModel.isPending}
              className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50 ml-auto">
              {updateModel.isPending ? 'Saving…' : 'Save changes'}
            </button>
          </div>
        </form>
      )}
    </div>
  )
}

// ─── Merge strategies ─────────────────────────────────────────────────────────

function MergeStrategiesSection({ owner, repo }: { owner: string; repo: string }) {
  const { data, isLoading } = useMergeStrategies(owner, repo)
  const updateStrategies = useUpdateMergeStrategies(owner, repo)
  const [form, setForm] = useState({ allowMergeCommit: true, allowSquash: true, allowRebase: true })
  const [saved, setSaved] = useState(false)
  const [error, setError] = useState('')

  useEffect(() => { if (data) setForm(data) }, [data])

  async function handleSave(e: React.FormEvent) {
    e.preventDefault()
    setError('')
    try {
      await updateStrategies.mutateAsync(form)
      setSaved(true)
      setTimeout(() => setSaved(false), 3000)
    } catch (err) { setError((err as Error).message) }
  }

  const strategies = [
    { key: 'allowMergeCommit' as const, label: 'Merge commit', description: 'Combines all commits from the branch into a merge commit on the target branch.', icon: 'M' },
    { key: 'allowSquash' as const, label: 'Squash', description: 'Squashes all branch commits into a single commit on the target branch.', icon: 'S' },
    { key: 'allowRebase' as const, label: 'Rebase', description: 'Replays each branch commit on top of the target branch without a merge commit.', icon: 'R' },
  ]

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Merge strategies</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">Control which merge strategies collaborators can use when closing pull requests.</p>
      </div>
      <div className="border-t border-[var(--c-border)]" />

      {isLoading ? <Skeleton className="h-48 w-full" /> : (
        <form onSubmit={handleSave} className="space-y-4">
          {strategies.map(s => (
            <label key={s.key} className="flex items-start gap-4 p-4 border border-[var(--c-border)] rounded-lg cursor-pointer hover:bg-[var(--c-surface-raised)] bg-[var(--c-surface)]">
              <input type="checkbox" checked={form[s.key]} onChange={e => setForm(f => ({ ...f, [s.key]: e.target.checked }))}
                className="mt-0.5 w-4 h-4 accent-[var(--c-brand)]" />
              <div className="flex-1">
                <div className="flex items-center gap-2">
                  <span className="w-6 h-6 rounded bg-[var(--c-brand-tint)] text-[var(--c-brand)] text-xs font-bold flex items-center justify-center">{s.icon}</span>
                  <span className="text-sm font-medium text-[var(--c-text)]">{s.label}</span>
                </div>
                <p className="text-xs text-[var(--c-muted)] mt-1">{s.description}</p>
              </div>
            </label>
          ))}

          {error && <p className="text-xs text-[var(--c-danger)]">{error}</p>}
          <div className="flex items-center gap-3 pt-2 border-t border-[var(--c-border)]">
            {saved && <span className="text-xs text-[var(--c-success)] font-medium">Saved!</span>}
            <button type="submit" disabled={updateStrategies.isPending}
              className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50 ml-auto">
              {updateStrategies.isPending ? 'Saving…' : 'Save changes'}
            </button>
          </div>
        </form>
      )}
    </div>
  )
}

// ─── Webhooks ─────────────────────────────────────────────────────────────────

const ALL_EVENTS = ['push', 'pull_request', 'issue']

function WebhooksSection({ owner, repo }: { owner: string; repo: string }) {
  const { data: hooks, isLoading } = useWebhooks(owner, repo)
  const createHook = useCreateWebhook(owner, repo)
  const updateHook = useUpdateWebhook(owner, repo)
  const deleteHook = useDeleteWebhook(owner, repo)
  const testHook = useTestWebhook(owner, repo)

  const [showForm, setShowForm] = useState(false)
  const [editId, setEditId] = useState<number | null>(null)
  const [title, setTitle] = useState('')
  const [url, setUrl] = useState('')
  const [secret, setSecret] = useState('')
  const [events, setEvents] = useState<string[]>(['push'])
  const [active, setActive] = useState(true)
  const [formError, setFormError] = useState('')
  const [testResult, setTestResult] = useState<Record<number, { ok: boolean; status: number }>>({})

  function resetForm() {
    setTitle(''); setUrl(''); setSecret(''); setEvents(['push']); setActive(true)
    setFormError(''); setEditId(null); setShowForm(false)
  }

  function startEdit(wh: { id: number; title: string; url: string; events: string; active: boolean }) {
    setEditId(wh.id); setTitle(wh.title); setUrl(wh.url)
    setEvents(wh.events.split(',')); setActive(wh.active); setSecret('')
    setShowForm(true)
  }

  function toggleEvent(ev: string) {
    setEvents(prev => prev.includes(ev) ? prev.filter(e => e !== ev) : [...prev, ev])
  }

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    setFormError('')
    if (!url.trim()) return setFormError('URL is required')
    if (events.length === 0) return setFormError('Select at least one event')
    try {
      const payload = { title: title.trim(), url: url.trim(), secret: secret || undefined, events: events.join(','), active }
      if (editId) {
        await updateHook.mutateAsync({ id: editId, ...payload })
      } else {
        await createHook.mutateAsync(payload)
      }
      resetForm()
    } catch (err) { setFormError((err as Error).message) }
  }

  async function handleTest(id: number) {
    const result = await testHook.mutateAsync(id)
    setTestResult(prev => ({ ...prev, [id]: result }))
    setTimeout(() => setTestResult(prev => { const n = { ...prev }; delete n[id]; return n }), 5000)
  }

  function statusBadge(status: number) {
    if (status === 0) return <span className="text-[10px] text-[var(--c-muted)]">Never delivered</span>
    const ok = status >= 200 && status < 300
    return (
      <span className={`text-[10px] font-semibold px-1.5 py-0.5 rounded-full ${ok ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300' : 'bg-[var(--c-danger-tint)] text-[var(--c-danger)]'}`}>
        {status}
      </span>
    )
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div className="flex items-start justify-between">
        <div>
          <h1 className="text-xl font-semibold text-[var(--c-text)]">Webhooks</h1>
          <p className="text-sm text-[var(--c-muted)] mt-1">Send HTTP POST notifications to external services when events occur.</p>
        </div>
        {!showForm && (
          <button onClick={() => setShowForm(true)}
            className="px-3 py-1.5 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] shrink-0">
            Add webhook
          </button>
        )}
      </div>
      <div className="border-t border-[var(--c-border)]" />

      {/* Webhook list */}
      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
        {isLoading ? (
          <div className="p-4 space-y-3">{[1,2].map(i => <Skeleton key={i} className="h-10 w-full" />)}</div>
        ) : !hooks?.length ? (
          <div className="px-4 py-8 text-center text-sm text-[var(--c-muted)]">No webhooks yet.</div>
        ) : (
          <ul className="divide-y divide-[var(--c-border)]">
            {hooks.map(wh => (
              <li key={wh.id} className="px-4 py-3 space-y-2">
                <div className="flex items-center gap-3">
                  <div className={`w-2 h-2 rounded-full shrink-0 ${wh.active ? 'bg-[var(--c-success)]' : 'bg-[var(--c-subtle)]'}`} />
                  <div className="flex-1 min-w-0">
                    {wh.title && <p className="text-sm font-medium text-[var(--c-text)]">{wh.title}</p>}
                    <p className="text-xs text-[var(--c-muted)] font-mono truncate">{wh.url}</p>
                  </div>
                  {statusBadge(wh.lastStatus)}
                  {testResult[wh.id] && (
                    <span className={`text-[10px] font-semibold ${testResult[wh.id].ok ? 'text-[var(--c-success)]' : 'text-[var(--c-danger)]'}`}>
                      Test: {testResult[wh.id].status || 'failed'}
                    </span>
                  )}
                </div>
                <div className="flex items-center gap-2 ml-5">
                  <div className="flex gap-1 flex-wrap">
                    {wh.events.split(',').map(ev => (
                      <span key={ev} className="text-[9px] font-semibold uppercase px-1.5 py-0.5 rounded bg-[var(--c-surface-muted)] text-[var(--c-muted)]">{ev}</span>
                    ))}
                  </div>
                  <div className="ml-auto flex items-center gap-1">
                    <button onClick={() => handleTest(wh.id)} disabled={testHook.isPending}
                      className="text-xs text-[var(--c-muted)] hover:text-[var(--c-text)] px-2 py-1 rounded hover:bg-[var(--c-surface-muted)] disabled:opacity-40">
                      Test
                    </button>
                    <button onClick={() => startEdit(wh)}
                      className="text-xs text-[var(--c-muted)] hover:text-[var(--c-text)] px-2 py-1 rounded hover:bg-[var(--c-surface-muted)]">
                      Edit
                    </button>
                    <button onClick={() => deleteHook.mutate(wh.id)} disabled={deleteHook.isPending}
                      className="text-[var(--c-danger)] hover:text-[var(--c-danger-dark)] p-1 rounded hover:bg-[var(--c-danger-tint)] disabled:opacity-40">
                      <svg width="13" height="13" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
                      </svg>
                    </button>
                  </div>
                </div>
              </li>
            ))}
          </ul>
        )}
      </div>

      {/* Add / Edit form */}
      {showForm && (
        <div className="border border-[var(--c-brand-focus)] rounded-lg overflow-hidden bg-[var(--c-surface)]">
          <div className="px-4 py-3 border-b border-[var(--c-border)] bg-[var(--c-surface-raised)]">
            <h2 className="text-sm font-semibold text-[var(--c-text)]">{editId ? 'Edit webhook' : 'Add webhook'}</h2>
          </div>
          <form onSubmit={handleSubmit} className="p-4 space-y-4">
            <div>
              <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Title <span className="text-[var(--c-subtle)]">(optional)</span></label>
              <input value={title} onChange={e => setTitle(e.target.value)} placeholder="e.g. Slack notifications"
                className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]" />
            </div>
            <div>
              <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Payload URL <span className="text-[var(--c-danger)]">*</span></label>
              <input value={url} onChange={e => setUrl(e.target.value)} placeholder="https://example.com/webhook"
                className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]" />
            </div>
            <div>
              <label className="block text-xs font-medium text-[var(--c-muted)] mb-1">Secret <span className="text-[var(--c-subtle)]">(optional — used for HMAC-SHA256 signature)</span></label>
              <input type="password" value={secret} onChange={e => setSecret(e.target.value)} placeholder={editId ? 'Leave blank to keep existing' : ''}
                className="w-full border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]" />
            </div>
            <div>
              <label className="block text-xs font-medium text-[var(--c-muted)] mb-2">Events</label>
              <div className="flex gap-3 flex-wrap">
                {ALL_EVENTS.map(ev => (
                  <label key={ev} className="flex items-center gap-1.5 cursor-pointer text-sm text-[var(--c-text)]">
                    <input type="checkbox" checked={events.includes(ev)} onChange={() => toggleEvent(ev)} className="w-4 h-4 accent-[var(--c-brand)]" />
                    {ev}
                  </label>
                ))}
              </div>
            </div>
            <label className="flex items-center gap-2 cursor-pointer text-sm text-[var(--c-text)]">
              <input type="checkbox" checked={active} onChange={e => setActive(e.target.checked)} className="w-4 h-4 accent-[var(--c-brand)]" />
              Active
            </label>
            {formError && <p className="text-xs text-[var(--c-danger)]">{formError}</p>}
            <div className="flex gap-2">
              <button type="submit" disabled={createHook.isPending || updateHook.isPending}
                className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50">
                {(createHook.isPending || updateHook.isPending) ? 'Saving…' : editId ? 'Update webhook' : 'Add webhook'}
              </button>
              <button type="button" onClick={resetForm}
                className="px-4 py-2 rounded border border-[var(--c-border)] text-sm text-[var(--c-text)] hover:bg-[var(--c-surface-muted)]">
                Cancel
              </button>
            </div>
          </form>
        </div>
      )}
    </div>
  )
}

// ─── Default reviewers ────────────────────────────────────────────────────────

function DefaultReviewersSection({ owner, repo }: { owner: string; repo: string }) {
  const { data: reviewers = [], isLoading } = useDefaultReviewers(owner, repo)
  const addReviewer = useAddDefaultReviewer(owner, repo)
  const removeReviewer = useRemoveDefaultReviewer(owner, repo)
  const [username, setUsername] = useState('')
  const [error, setError] = useState('')

  async function handleAdd(e: React.FormEvent) {
    e.preventDefault()
    setError('')
    const u = username.trim()
    if (!u) return
    try {
      await addReviewer.mutateAsync(u)
      setUsername('')
    } catch (err: any) {
      setError(err.message ?? 'Failed to add reviewer')
    }
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Default reviewers</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">These users are automatically added as reviewers whenever a pull request is opened in this repository.</p>
      </div>

      <form onSubmit={handleAdd} className="flex gap-2">
        <input
          value={username}
          onChange={e => setUsername(e.target.value)}
          placeholder="Username"
          className="flex-1 border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)]"
        />
        <button
          type="submit"
          disabled={addReviewer.isPending || !username.trim()}
          className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50"
        >
          {addReviewer.isPending ? 'Adding…' : 'Add reviewer'}
        </button>
      </form>
      {error && <p className="text-xs text-[var(--c-danger)]">{error}</p>}

      <div className="border border-[var(--c-border)] rounded-lg overflow-hidden">
        {isLoading ? (
          <div className="p-4 space-y-3">
            {[0,1,2].map(i => <Skeleton key={i} className="h-10 rounded" />)}
          </div>
        ) : reviewers.length === 0 ? (
          <div className="p-8 text-center text-sm text-[var(--c-muted)]">No default reviewers configured.</div>
        ) : (
          <ul className="divide-y divide-[var(--c-border)]">
            {reviewers.map(rv => (
              <li key={rv.userId} className="flex items-center justify-between px-4 py-3">
                <div className="flex items-center gap-3">
                  {rv.avatarUrl ? (
                    <img src={rv.avatarUrl} alt={rv.username} className="w-8 h-8 rounded-full object-cover" />
                  ) : (
                    <div className="w-8 h-8 rounded-full bg-[var(--c-brand)] flex items-center justify-center text-white text-xs font-semibold">
                      {rv.username.slice(0, 1).toUpperCase()}
                    </div>
                  )}
                  <span className="text-sm font-medium text-[var(--c-text)]">{rv.username}</span>
                </div>
                <button
                  onClick={() => removeReviewer.mutate(rv.username)}
                  disabled={removeReviewer.isPending}
                  className="text-xs text-[var(--c-danger)] hover:underline disabled:opacity-50"
                >
                  Remove
                </button>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  )
}

// ─── Default description ──────────────────────────────────────────────────────

function DefaultDescriptionSection({ owner, repo }: { owner: string; repo: string }) {
  const { data, isLoading } = useDefaultDescription(owner, repo)
  const updateDesc = useUpdateDefaultDescription(owner, repo)
  const [template, setTemplate] = useState('')
  const [saved, setSaved] = useState(false)

  useEffect(() => {
    if (data) setTemplate(data.template ?? '')
  }, [data])

  async function handleSave(e: React.FormEvent) {
    e.preventDefault()
    await updateDesc.mutateAsync(template)
    setSaved(true)
    setTimeout(() => setSaved(false), 2000)
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Default description</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">This template pre-fills the pull request body when a new PR is opened. Supports Markdown.</p>
      </div>

      {isLoading ? (
        <Skeleton className="h-48 rounded" />
      ) : (
        <form onSubmit={handleSave} className="space-y-4">
          <textarea
            value={template}
            onChange={e => setTemplate(e.target.value)}
            rows={12}
            placeholder={'## Summary\n\n## Test plan\n\n## Screenshots'}
            className="w-full border border-[var(--c-border)] rounded px-3 py-2.5 text-sm font-mono bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)] resize-y"
          />
          <div className="flex items-center gap-3">
            <button
              type="submit"
              disabled={updateDesc.isPending}
              className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50"
            >
              {updateDesc.isPending ? 'Saving…' : 'Save template'}
            </button>
            {saved && <span className="text-xs text-[var(--c-success)]">Saved</span>}
          </div>
        </form>
      )}
    </div>
  )
}

// ─── Excluded files ───────────────────────────────────────────────────────────

function ExcludedFilesSection({ owner, repo }: { owner: string; repo: string }) {
  const { data, isLoading } = useExcludedFiles(owner, repo)
  const updateFiles = useUpdateExcludedFiles(owner, repo)
  const [patterns, setPatterns] = useState('')
  const [saved, setSaved] = useState(false)

  useEffect(() => {
    if (data) setPatterns(data.patterns ?? '')
  }, [data])

  async function handleSave(e: React.FormEvent) {
    e.preventDefault()
    await updateFiles.mutateAsync(patterns)
    setSaved(true)
    setTimeout(() => setSaved(false), 2000)
  }

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Excluded files</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">
          Files matching these glob patterns are excluded from pull request diff views. One pattern per line.
          Example: <code className="text-xs bg-[var(--c-surface-muted)] px-1 py-0.5 rounded font-mono">package-lock.json</code>
        </p>
      </div>

      {isLoading ? (
        <Skeleton className="h-48 rounded" />
      ) : (
        <form onSubmit={handleSave} className="space-y-4">
          <textarea
            value={patterns}
            onChange={e => setPatterns(e.target.value)}
            rows={10}
            placeholder={'package-lock.json\nyarn.lock\ndist/**\n*.min.js'}
            className="w-full border border-[var(--c-border)] rounded px-3 py-2.5 text-sm font-mono bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)] resize-y"
          />
          <div className="text-xs text-[var(--c-muted)]">
            Patterns use glob syntax. <code className="font-mono">*</code> matches any file, <code className="font-mono">**</code> matches any path segment.
          </div>
          <div className="flex items-center gap-3">
            <button
              type="submit"
              disabled={updateFiles.isPending}
              className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50"
            >
              {updateFiles.isPending ? 'Saving…' : 'Save patterns'}
            </button>
            {saved && <span className="text-xs text-[var(--c-success)]">Saved</span>}
          </div>
        </form>
      )}
    </div>
  )
}

// ─── Git LFS ─────────────────────────────────────────────────────────────────

function GitLFSSection({ owner, repo }: { owner: string; repo: string }) {
  const { data, isLoading } = useLFSSettings(owner, repo)
  const update = useUpdateLFSSettings(owner, repo)
  const [maxSizeInput, setMaxSizeInput] = useState('')
  const [sizeError, setSizeError] = useState('')
  const [saved, setSaved] = useState(false)

  useEffect(() => {
    if (data) setMaxSizeInput(data.maxFileSizeMB === 0 ? '' : String(data.maxFileSizeMB))
  }, [data])

  async function toggle(field: 'enabled' | 'lockingEnabled', value: boolean) {
    await update.mutateAsync({ [field]: value })
  }

  async function saveMaxSize(e: React.FormEvent) {
    e.preventDefault()
    setSizeError('')
    const raw = maxSizeInput.trim()
    const mb = raw === '' ? 0 : parseInt(raw, 10)
    if (raw !== '' && (isNaN(mb) || mb < 0)) {
      setSizeError('Enter a positive number of megabytes, or leave blank for unlimited.')
      return
    }
    await update.mutateAsync({ maxFileSizeMB: mb })
    setSaved(true)
    setTimeout(() => setSaved(false), 2000)
  }

  if (isLoading) {
    return (
      <div className="max-w-2xl px-6 py-6 space-y-4">
        <Skeleton className="h-6 w-48 rounded" />
        <Skeleton className="h-24 rounded" />
        <Skeleton className="h-24 rounded" />
      </div>
    )
  }

  const enabled = data?.enabled ?? false
  const lockingEnabled = data?.lockingEnabled ?? true

  return (
    <div className="max-w-2xl px-6 py-6 space-y-6">
      <div>
        <h1 className="text-xl font-semibold text-[var(--c-text)]">Git LFS</h1>
        <p className="text-sm text-[var(--c-muted)] mt-1">
          Git Large File Storage replaces large binary files (images, videos, datasets) with lightweight text pointers inside Git,
          while storing the actual file content on the server.
        </p>
      </div>

      {/* Enable / disable */}
      <div className="border border-[var(--c-border)] rounded-lg divide-y divide-[var(--c-border)]">
        <div className="flex items-start justify-between gap-4 px-4 py-4">
          <div>
            <p className="text-sm font-medium text-[var(--c-text)]">Enable Git LFS</p>
            <p className="text-xs text-[var(--c-muted)] mt-0.5">
              Allow contributors to push large files using the LFS protocol.
            </p>
          </div>
          <button
            onClick={() => toggle('enabled', !enabled)}
            disabled={update.isPending}
            className={`relative shrink-0 inline-flex h-6 w-11 items-center rounded-full transition-colors disabled:opacity-50 ${enabled ? 'bg-[var(--c-brand)]' : 'bg-[var(--c-border)]'}`}
          >
            <span className={`inline-block h-4 w-4 rounded-full bg-white shadow transition-transform ${enabled ? 'translate-x-6' : 'translate-x-1'}`} />
          </button>
        </div>

        <div className={`flex items-start justify-between gap-4 px-4 py-4 transition-opacity ${enabled ? '' : 'opacity-40 pointer-events-none'}`}>
          <div>
            <p className="text-sm font-medium text-[var(--c-text)]">File locking</p>
            <p className="text-xs text-[var(--c-muted)] mt-0.5">
              Allow contributors to lock LFS files to prevent conflicting edits on binary assets.
            </p>
          </div>
          <button
            onClick={() => toggle('lockingEnabled', !lockingEnabled)}
            disabled={update.isPending || !enabled}
            className={`relative shrink-0 inline-flex h-6 w-11 items-center rounded-full transition-colors disabled:opacity-50 ${lockingEnabled ? 'bg-[var(--c-brand)]' : 'bg-[var(--c-border)]'}`}
          >
            <span className={`inline-block h-4 w-4 rounded-full bg-white shadow transition-transform ${lockingEnabled ? 'translate-x-6' : 'translate-x-1'}`} />
          </button>
        </div>
      </div>

      {/* Max file size */}
      <div className={`space-y-3 transition-opacity ${enabled ? '' : 'opacity-40 pointer-events-none'}`}>
        <div>
          <p className="text-sm font-medium text-[var(--c-text)]">Maximum file size</p>
          <p className="text-xs text-[var(--c-muted)] mt-0.5">
            Reject LFS pushes for individual files exceeding this size. Leave blank for no limit.
          </p>
        </div>
        <form onSubmit={saveMaxSize} className="flex items-center gap-2">
          <div className="relative">
            <input
              type="number"
              min="1"
              value={maxSizeInput}
              onChange={e => setMaxSizeInput(e.target.value)}
              disabled={!enabled}
              placeholder="Unlimited"
              className="w-36 border border-[var(--c-border)] rounded px-3 py-2 text-sm bg-[var(--c-surface)] text-[var(--c-text)] focus:outline-none focus:border-[var(--c-brand-focus)] disabled:opacity-50 pr-10"
            />
            <span className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-[var(--c-muted)] pointer-events-none">MB</span>
          </div>
          <button
            type="submit"
            disabled={update.isPending || !enabled}
            className="px-4 py-2 rounded bg-[var(--c-brand)] text-white text-sm font-medium hover:bg-[var(--c-brand-hover)] disabled:opacity-50"
          >
            {update.isPending ? 'Saving…' : 'Save'}
          </button>
          {saved && <span className="text-xs text-[var(--c-success)]">Saved</span>}
        </form>
        {sizeError && <p className="text-xs text-[var(--c-danger)]">{sizeError}</p>}
      </div>

      {/* Info box */}
      <div className="rounded-lg bg-[var(--c-surface-muted)] border border-[var(--c-border)] px-4 py-3 flex gap-3">
        <svg className="shrink-0 mt-0.5 text-[var(--c-brand)]" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
        </svg>
        <p className="text-xs text-[var(--c-muted)] leading-relaxed">
          Git LFS must be <a href="https://git-lfs.com" target="_blank" rel="noreferrer" className="text-[var(--c-brand)] hover:underline">installed on the client</a> to push and pull tracked files.
          Add a <code className="font-mono bg-[var(--c-surface)] px-1 rounded">.gitattributes</code> file to your repository to specify which file patterns LFS should track.
        </p>
      </div>
    </div>
  )
}

// ─── Coming soon ──────────────────────────────────────────────────────────────

function ComingSoon({ sectionId }: { sectionId: SectionId }) {
  const meta = SECTION_META[sectionId]
  return (
    <div className="max-w-2xl px-6 py-6">
      <h1 className="text-xl font-semibold text-[var(--c-text)] mb-1">{meta.title}</h1>
      <div className="mt-8 flex flex-col items-center justify-center py-16 border border-dashed border-[var(--c-border)] rounded-lg text-center bg-[var(--c-surface-raised)]">
        <svg width="40" height="40" fill="none" stroke="var(--c-subtle)" strokeWidth="1.2" viewBox="0 0 24 24" className="mb-4">
          <path strokeLinecap="round" strokeLinejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75" />
        </svg>
        <h2 className="text-base font-semibold text-[var(--c-text)] mb-2">{meta.title}</h2>
        <p className="text-sm text-[var(--c-muted)] max-w-sm leading-relaxed">{meta.description}</p>
        <span className="mt-5 text-[10px] font-semibold uppercase tracking-wider text-white bg-[var(--c-subtle)] px-2.5 py-1 rounded-full">Coming soon</span>
      </div>
    </div>
  )
}
