'use client'

/**
 * Inline SEO editor pane for the post/page section editors and the Content SEO
 * list. Accordion sections mirror Yoast-style workflow: Focus Keyword → Search
 * Appearance → Social → Analysis → Advanced → Insights → Linking.
 */
import {
  cloneElement,
  isValidElement,
  useEffect,
  useId,
  useMemo,
  useState,
  type ReactElement,
  type ReactNode,
} from 'react'
import { Loader2, Monitor, Smartphone, Sparkles, Wand2 } from 'lucide-react'
import { toast } from 'sonner'
import type { BrandAlignmentScoreResult } from '@actuate-media/cms-core/brand-voice'
import { resolveRenderedMetaTitle } from '@actuate-media/cms-core/client'
import {
  analyzeContent,
  calculateReadability,
  stripHtmlTags,
  type SEOAnalysisResult,
} from '@actuate-media/cms-core/seo/analysis'
import {
  fetchSeoContentRecord,
  updateSeoFields,
  generateSeoField,
  fetchDocumentSeoAnalysis,
  SEO_SCHEMA_TYPES,
  type SeoContentRecord,
  type SeoFieldPayload,
  type LlmsInclude,
} from '../../lib/seo-service.js'
import { fixBrandVoiceContent } from '../../lib/brand-voice-client.js'
import { BrandVoiceAlignmentBadge } from '../BrandVoiceAlignmentBadge.js'
import { InspectorPaneBody, InspectorPaneFooter, InspectorPaneHeader } from '../ui/InspectorPane.js'
import { Toggle } from '../ui/Toggle.js'
import { SeoLoading, SeoErrorState, gradeMeta } from './primitives.js'
import { SeoAccordion, SeoAccordionBadge, SeoAccordionItem } from './SeoAccordion.js'
import { SeoAnalysisList } from './SeoAnalysisList.js'
import { SeoInsightsMetrics } from './SeoInsightsMetrics.js'
import { SeoLinkingSection } from './SeoLinkingSection.js'

const META_TITLE_MAX = 60
const META_DESC_MAX = 160
const KEY_TAKEAWAY_MAX = 240
const LLMS_DESC_MAX = 160

type AiField =
  | 'metaTitle'
  | 'metaDescription'
  | 'ogTitle'
  | 'ogDescription'
  | 'focusKeyword'
  | 'keyTakeaway'

function seoBrandChannel(field: AiField): string | undefined {
  if (field === 'metaDescription' || field === 'ogDescription') return 'meta_description'
  return undefined
}

const inputCls =
  'w-full rounded-[7px] border border-[var(--bdr)] bg-[var(--card)] px-2.5 py-1.5 text-[12.5px] text-[var(--txt)] placeholder:text-[var(--muted)] focus:border-[var(--acc)] focus:outline-none'

function CharCount({ value, max }: { value: string; max: number }) {
  const over = value.length > max
  return (
    <span className={`text-[11px] ${over ? 'text-[var(--err)]' : 'text-[var(--muted)]'}`}>
      {value.length}/{max}
    </span>
  )
}

function PaneField({
  label,
  children,
  hint,
  help,
}: {
  label: string
  children: ReactNode
  hint?: ReactNode
  help?: string
}) {
  const generatedId = useId()
  let control = children
  if (isValidElement(children) && (children.props as { id?: string }).id == null) {
    control = cloneElement(children as ReactElement<{ id?: string }>, { id: generatedId })
  }
  const controlId = isValidElement(control) ? (control.props as { id?: string }).id : undefined

  return (
    <div className="mb-3 last:mb-0">
      <div className="mb-1 flex items-center justify-between gap-2">
        <label htmlFor={controlId} className="text-[11.5px] font-medium text-[var(--sub)]">
          {label}
        </label>
        {hint}
      </div>
      {control}
      {help && <p className="mt-1 text-[11px] text-[var(--muted)]">{help}</p>}
    </div>
  )
}

function PaneToggleRow({
  label,
  hint,
  checked,
  onCheckedChange,
}: {
  label: string
  hint: string
  checked: boolean
  onCheckedChange: (checked: boolean) => void
}) {
  return (
    <div className="flex items-center justify-between gap-3 py-1.5">
      <div className="min-w-0">
        <div className="text-[12.5px] font-medium text-[var(--txt)]">{label}</div>
        <div className="text-[11px] text-[var(--muted)]">{hint}</div>
      </div>
      <Toggle checked={checked} onCheckedChange={onCheckedChange} aria-label={label} />
    </div>
  )
}

export interface SeoEditorPaneProps {
  entityType: 'page' | 'post'
  /** When absent, draft-only mode — meta fields sync via `onDraftChange`. */
  entityId: string | null
  onClose: () => void
  onSaved?: () => void
  /** Live HTML from the section editor for real-time analysis. */
  contentHtml?: string
  slug?: string
  draftMeta?: { seoTitle: string; seoDescription: string }
  onDraftChange?: (patch: { seoTitle?: string; seoDescription?: string }) => void
}

export function SeoEditorPane({
  entityType,
  entityId,
  onClose,
  onSaved,
  contentHtml = '',
  slug = '',
  draftMeta,
  onDraftChange,
}: SeoEditorPaneProps) {
  const isDraft = !entityId
  const [record, setRecord] = useState<SeoContentRecord | null>(null)
  const [loading, setLoading] = useState(!isDraft)
  const [error, setError] = useState<string | null>(null)
  const [saving, setSaving] = useState(false)
  const [aiBusy, setAiBusy] = useState<AiField | null>(null)
  const [aiFixBusy, setAiFixBusy] = useState<AiField | null>(null)
  const [aiAlignments, setAiAlignments] = useState<
    Partial<Record<AiField, BrandAlignmentScoreResult>>
  >({})
  const [form, setForm] = useState<SeoFieldPayload>({})
  const [previewDevice, setPreviewDevice] = useState<'mobile' | 'desktop'>('mobile')
  const [socialPlatform, setSocialPlatform] = useState<'facebook' | 'twitter'>('facebook')
  const [remoteAnalysis, setRemoteAnalysis] = useState<SEOAnalysisResult | null>(null)

  useEffect(() => {
    if (isDraft) {
      setLoading(false)
      setRecord(null)
      setError(null)
      setForm({
        metaTitle: draftMeta?.seoTitle ?? '',
        metaDescription: draftMeta?.seoDescription ?? '',
        focusKeyword: '',
        keyTakeaway: '',
        ogTitle: '',
        ogDescription: '',
        canonicalUrl: '',
        structuredDataType: 'WebPage',
        noindex: false,
        nofollow: false,
        llmsInclude: 'AUTO',
        llmsDescription: '',
        llmsSection: '',
        llmsOptional: false,
      })
      return
    }

    let alive = true
    setLoading(true)
    setError(null)
    fetchSeoContentRecord(entityType, entityId)
      .then((rec) => {
        if (!alive) return
        if (!rec) {
          setError('Could not load this record.')
          return
        }
        setRecord(rec)
        setForm({
          metaTitle: rec.metaTitlePage ?? '',
          metaDescription: rec.metaDescription ?? '',
          canonicalUrl: rec.canonicalUrl ?? '',
          focusKeyword: rec.focusKeyword ?? '',
          keyTakeaway: rec.keyTakeaway ?? '',
          ogTitle: rec.ogTitle ?? '',
          ogDescription: rec.ogDescription ?? '',
          twitterTitle: rec.twitterTitle ?? '',
          twitterDescription: rec.twitterDescription ?? '',
          structuredDataType: rec.structuredDataType ?? 'WebPage',
          noindex: rec.noindex,
          nofollow: rec.nofollow,
          llmsInclude: rec.llmsInclude ?? 'AUTO',
          llmsDescription: rec.llmsDescription ?? '',
          llmsSection: rec.llmsSection ?? '',
          llmsOptional: rec.llmsOptional ?? false,
        })
      })
      .catch((e: unknown) => alive && setError(e instanceof Error ? e.message : 'Failed to load'))
      .finally(() => alive && setLoading(false))
    return () => {
      alive = false
    }
  }, [entityType, entityId, isDraft])

  useEffect(() => {
    if (isDraft || contentHtml.trim() || !entityId) return
    let alive = true
    fetchDocumentSeoAnalysis(entityId)
      .then((result) => alive && setRemoteAnalysis(result))
      .catch(() => alive && setRemoteAnalysis(null))
    return () => {
      alive = false
    }
  }, [entityId, isDraft, contentHtml])

  const update = (patch: Partial<SeoFieldPayload>) => {
    setForm((f) => {
      const next = { ...f, ...patch }
      if (isDraft && onDraftChange) {
        if ('metaTitle' in patch) onDraftChange({ seoTitle: patch.metaTitle })
        if ('metaDescription' in patch) onDraftChange({ seoDescription: patch.metaDescription })
      }
      return next
    })
  }

  async function handleAiGenerate(field: AiField) {
    if (isDraft && !contentHtml.trim() && field !== 'focusKeyword') {
      toast.error('Add a title or body content before generating with AI.')
      return
    }

    setAiBusy(field)
    try {
      if (isDraft) {
        toast.error('Save a draft first to use AI field generation with site knowledge.')
        return
      }

      const result = await generateSeoField(field, { entityType, entityId: entityId! })
      if (result.unavailable) {
        toast.error('AI is not configured. Add an AI provider to enable suggestions.')
        return
      }
      if (result.text) {
        // Write the suggestion into the field the editor actually asked to
        // generate. Previously an OG title/description generate was remapped
        // onto metaTitle/metaDescription (and the OG field cleared), silently
        // destroying a hand-written search title with no undo. Social fields
        // still inherit meta at render time when left blank, so filling the OG
        // field explicitly is non-destructive and matches the button's intent.
        const patch: Partial<SeoFieldPayload> = { [field]: result.text }
        update(patch)
        if (result.brandAlignment) {
          setAiAlignments((prev) => ({ ...prev, [field]: result.brandAlignment }))
        }
        toast.success('AI suggestion applied — review before saving.')
      } else {
        toast.error('No suggestion was returned.')
      }
    } catch {
      toast.error('AI request failed.')
    } finally {
      setAiBusy(null)
    }
  }

  async function handleAiFix(field: AiField) {
    const text = (form[field as keyof SeoFieldPayload] as string | undefined) ?? ''
    if (!text.trim()) return
    setAiFixBusy(field)
    try {
      const res = await fixBrandVoiceContent({
        content: text,
        channel: seoBrandChannel(field),
      })
      if (!res.ok || !res.result) {
        toast.error(res.error ?? 'Could not fix copy for brand voice.')
        return
      }
      update({ [field]: res.result.text } as Partial<SeoFieldPayload>)
      setAiAlignments((prev) => ({ ...prev, [field]: res.result!.afterScore }))
      toast.success('Copy adjusted to match brand voice.')
    } finally {
      setAiFixBusy(null)
    }
  }

  function alignmentRow(field: AiField) {
    const alignment = aiAlignments[field]
    if (!alignment) return null
    const fixing = aiFixBusy === field
    return (
      <div className="mt-2 flex flex-wrap items-center gap-2">
        <BrandVoiceAlignmentBadge alignment={alignment} compact />
        {alignment.score < 85 && (
          <button
            type="button"
            onClick={() => void handleAiFix(field)}
            disabled={fixing || aiBusy !== null}
            className="inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-[11.5px] font-medium text-[var(--acc)] transition-colors duration-[var(--motion-fast)] hover:bg-[var(--acc-l)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:opacity-60"
          >
            {fixing ? (
              <Loader2 className="h-3.5 w-3.5 animate-spin" aria-hidden />
            ) : (
              <Wand2 className="h-3.5 w-3.5" aria-hidden />
            )}
            Fix for brand voice
          </button>
        )}
      </div>
    )
  }

  async function handleSave() {
    if (isDraft) {
      toast.success('SEO fields will be saved with the document.')
      onClose()
      return
    }
    setSaving(true)
    const res = await updateSeoFields(entityType, entityId!, form)
    setSaving(false)
    if (res.error) {
      toast.error(res.error)
      return
    }
    toast.success('SEO fields saved.')
    onSaved?.()
  }

  const renderedTitle = useMemo(() => {
    if (isDraft) return form.metaTitle?.trim() || draftMeta?.seoTitle || 'Untitled'
    if (!record) return form.metaTitle?.trim() || 'Untitled'
    return resolveRenderedMetaTitle({
      metaTitlePage: form.metaTitle,
      documentTitle: record.title,
      site: {
        siteName: record.siteName ?? undefined,
        titleTemplate: record.titleTemplate ?? undefined,
      },
    })
  }, [form.metaTitle, record, isDraft, draftMeta?.seoTitle])

  const desc =
    form.metaDescription ||
    'Please provide a meta description. Search engines may generate one from your content.'
  const url = record?.url ?? (slug ? `/${slug.replace(/^\//, '')}` : '')
  const displayUrl = record?.url ?? url

  const analysisInput = useMemo(
    () => ({
      title: record?.title ?? draftMeta?.seoTitle ?? '',
      slug: slug || record?.url?.split('/').filter(Boolean).pop() || '',
      content: contentHtml,
      // Measure the FULL rendered title (site template applied), matching what
      // the page score (`calculatePageSeoScore`) and the server /seo/analysis
      // endpoint measure — otherwise the meta-title-length check gives the
      // editor advice that contradicts the list score for the same field.
      metaTitle: renderedTitle,
      metaDescription: form.metaDescription,
      focusKeyphrase: form.focusKeyword,
      canonical: form.canonicalUrl,
      ogTitle: form.ogTitle,
      ogDescription: form.ogDescription,
      ogImage: record?.ogImage ?? undefined,
    }),
    [record, draftMeta, slug, contentHtml, form, renderedTitle],
  )

  const liveAnalysis = useMemo(() => {
    if (contentHtml.trim()) return analyzeContent(analysisInput)
    return null
  }, [analysisInput, contentHtml])

  const analysis = liveAnalysis ?? remoteAnalysis
  const plainText = stripHtmlTags(contentHtml)
  const readability = useMemo(() => calculateReadability(plainText), [plainText])

  const problemCount = analysis?.checks.filter((c) => c.status === 'problem').length ?? 0
  const internalLinks =
    analysis?.checks.find((c) => c.id === 'internal-links')?.description.match(/\d+/)?.[0] ?? '0'
  const externalLinks =
    analysis?.checks.find((c) => c.id === 'outbound-links')?.description.match(/\d+/)?.[0] ?? '0'
  const internalOk = Number(internalLinks) >= 1
  const externalOk = Number(externalLinks) >= 1

  function aiButton(field: AiField, label?: string) {
    return (
      <button
        type="button"
        onClick={() => void handleAiGenerate(field)}
        disabled={aiBusy !== null || isDraft}
        title={isDraft ? 'Save a draft first' : label}
        className="inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] font-medium text-[var(--acc)] transition-colors duration-[var(--motion-fast)] hover:bg-[var(--acc-l)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:opacity-60"
      >
        <Sparkles className={`h-3 w-3 ${aiBusy === field ? 'animate-pulse' : ''}`} aria-hidden />
        {aiBusy === field ? 'Generating…' : 'AI Generate'}
      </button>
    )
  }

  const socialTitle =
    socialPlatform === 'twitter'
      ? form.twitterTitle || form.ogTitle || renderedTitle
      : form.ogTitle || renderedTitle
  const socialDesc =
    socialPlatform === 'twitter'
      ? form.twitterDescription || form.ogDescription || form.metaDescription || desc
      : form.ogDescription || form.metaDescription || desc

  return (
    <>
      <InspectorPaneHeader onClose={onClose} closeLabel="Close SEO editor">
        <div className="truncate text-[13.5px] font-semibold text-[var(--txt)]">
          {record?.title ?? draftMeta?.seoTitle ?? 'SEO'}
        </div>
        {displayUrl && (
          <div className="mt-0.5 truncate text-[11.5px] text-[var(--muted)]">{displayUrl}</div>
        )}
      </InspectorPaneHeader>

      <InspectorPaneBody>
        {loading ? (
          <div className="px-5 py-4">
            <SeoLoading />
          </div>
        ) : error ? (
          <div className="px-5 py-4">
            <SeoErrorState message={error} />
          </div>
        ) : (
          <>
            {isDraft && (
              <div className="border-b border-[var(--bdr)] px-5 py-3">
                <p className="text-[11.5px] text-[var(--sub)]">
                  Save a draft to unlock AI assists trained on your site knowledge and tone. You can
                  still set focus keyword, search appearance, and review live analysis below.
                </p>
              </div>
            )}

            {!isDraft && record && (
              <div className="border-b border-[var(--bdr)] px-5 py-3">
                {(() => {
                  const meta = gradeMeta(record.seoGrade)
                  return (
                    <div
                      className={`flex items-center gap-2 rounded-[7px] px-3 py-2 text-[12px] ${meta.bg} ${meta.text}`}
                    >
                      <span>
                        SEO score: <span className="font-medium">{record.seoScore}</span> (
                        {meta.label})
                        {analysis && (
                          <>
                            {' · '}
                            <span title="Content quality from the analysis checklist below (readability, keyphrase, links). Separate from the meta-weighted SEO score used to rank this list.">
                              Content analysis {analysis.score}/100
                            </span>
                          </>
                        )}
                      </span>
                    </div>
                  )
                })()}
              </div>
            )}

            <SeoAccordion>
              <SeoAccordionItem
                value="focus-keyword"
                title="Focus keyword"
                badge={
                  form.focusKeyword ? (
                    <SeoAccordionBadge tone="neutral">{form.focusKeyword}</SeoAccordionBadge>
                  ) : (
                    <SeoAccordionBadge tone="warn">Not set</SeoAccordionBadge>
                  )
                }
              >
                <PaneField
                  label="Focus keyphrase"
                  hint={aiButton('focusKeyword', 'Suggest focus keyphrase')}
                  help="The main phrase you want this content found for in search and AI answers."
                >
                  <input
                    className={inputCls}
                    value={form.focusKeyword ?? ''}
                    onChange={(e) => update({ focusKeyword: e.target.value })}
                    placeholder="e.g. modern headless cms"
                  />
                </PaneField>
                <PaneField
                  label="Key takeaway"
                  hint={
                    <span className="flex items-center gap-2">
                      <CharCount value={form.keyTakeaway ?? ''} max={KEY_TAKEAWAY_MAX} />
                      {aiButton('keyTakeaway')}
                    </span>
                  }
                  help="One or two sentences AI answer engines can quote — what is this page about?"
                >
                  <textarea
                    className={`${inputCls} min-h-16 resize-y`}
                    value={form.keyTakeaway ?? ''}
                    onChange={(e) => update({ keyTakeaway: e.target.value })}
                    placeholder="Summarize the page's core value proposition"
                  />
                </PaneField>
              </SeoAccordionItem>

              <SeoAccordionItem value="search-appearance" title="Search appearance">
                <p className="text-[11.5px] text-[var(--sub)]">
                  Determine how this content should look in search results.
                </p>
                <div className="flex items-center justify-between gap-2">
                  <span className="text-[11.5px] font-medium text-[var(--sub)]">
                    Google preview
                  </span>
                  <div className="flex rounded-[7px] border border-[var(--bdr)] p-0.5">
                    <button
                      type="button"
                      aria-pressed={previewDevice === 'mobile'}
                      onClick={() => setPreviewDevice('mobile')}
                      className={`inline-flex items-center gap-1 rounded-[5px] px-2 py-1 text-[11px] ${previewDevice === 'mobile' ? 'bg-[var(--acc-l)] text-[var(--acc)]' : 'text-[var(--muted)]'}`}
                    >
                      <Smartphone size={14} aria-hidden />
                      Mobile
                    </button>
                    <button
                      type="button"
                      aria-pressed={previewDevice === 'desktop'}
                      onClick={() => setPreviewDevice('desktop')}
                      className={`inline-flex items-center gap-1 rounded-[5px] px-2 py-1 text-[11px] ${previewDevice === 'desktop' ? 'bg-[var(--acc-l)] text-[var(--acc)]' : 'text-[var(--muted)]'}`}
                    >
                      <Monitor size={14} aria-hidden />
                      Desktop
                    </button>
                  </div>
                </div>
                <div
                  className={`rounded-[7px] border border-[var(--bdr)] bg-[var(--card)] p-3 ${previewDevice === 'mobile' ? 'max-w-[320px]' : ''}`}
                >
                  <p className="truncate text-[11px] text-[var(--suc)]">{displayUrl}</p>
                  <p className="text-[15px] leading-snug text-[var(--acc)]">{renderedTitle}</p>
                  <p className="line-clamp-2 text-[12px] text-[var(--sub)]">{desc}</p>
                </div>
                <PaneField
                  label="SEO title"
                  hint={
                    <span className="flex items-center gap-2">
                      <CharCount value={form.metaTitle ?? ''} max={META_TITLE_MAX} />
                      {aiButton('metaTitle')}
                    </span>
                  }
                >
                  <input
                    className={inputCls}
                    value={form.metaTitle ?? ''}
                    onChange={(e) => update({ metaTitle: e.target.value })}
                    placeholder="Page-specific title (%page% in your site template)"
                  />
                </PaneField>
                {alignmentRow('metaTitle')}
                <PaneField
                  label="Slug"
                  help="URL slug for this content — edit in page/post settings."
                >
                  <input className={`${inputCls} opacity-80`} value={slug} readOnly disabled />
                </PaneField>
                <PaneField
                  label="Meta description"
                  hint={
                    <span className="flex items-center gap-2">
                      <CharCount value={form.metaDescription ?? ''} max={META_DESC_MAX} />
                      {aiButton('metaDescription')}
                    </span>
                  }
                >
                  <textarea
                    className={`${inputCls} min-h-20 resize-y`}
                    value={form.metaDescription ?? ''}
                    onChange={(e) => update({ metaDescription: e.target.value })}
                    placeholder="Short summary shown under the title in search results"
                  />
                </PaneField>
                {alignmentRow('metaDescription')}
              </SeoAccordionItem>

              <SeoAccordionItem value="social-appearance" title="Social appearance">
                <p className="text-[11.5px] text-[var(--sub)]">
                  Customize title, description, and image used when sharing on social platforms.
                </p>
                <div className="flex rounded-[7px] border border-[var(--bdr)] p-0.5">
                  {(['facebook', 'twitter'] as const).map((platform) => (
                    <button
                      key={platform}
                      type="button"
                      aria-pressed={socialPlatform === platform}
                      onClick={() => setSocialPlatform(platform)}
                      className={`flex-1 rounded-[5px] px-2 py-1.5 text-[11.5px] font-medium capitalize ${socialPlatform === platform ? 'bg-[var(--acc-l)] text-[var(--acc)]' : 'text-[var(--sub)]'}`}
                    >
                      {platform === 'twitter' ? 'X / Twitter' : 'Facebook'}
                    </button>
                  ))}
                </div>
                <div className="overflow-hidden rounded-[7px] border border-[var(--bdr)]">
                  {record?.ogImage ? (
                    // eslint-disable-next-line @next/next/no-img-element
                    <img
                      src={record.ogImage}
                      alt=""
                      className="aspect-[1200/630] w-full bg-[var(--bg)] object-cover"
                    />
                  ) : (
                    <div className="flex aspect-[1200/630] w-full items-center justify-center bg-[var(--bg)] text-[12px] text-[var(--muted)]">
                      No social image — add a featured or OG image
                    </div>
                  )}
                  <div className="p-2.5">
                    <p className="truncate text-[10.5px] text-[var(--muted)] uppercase">
                      {displayUrl.replace(/^https?:\/\//, '').split('/')[0] || 'yoursite.com'}
                    </p>
                    <p className="truncate text-[12.5px] font-medium text-[var(--txt)]">
                      {socialTitle}
                    </p>
                    <p className="line-clamp-2 text-[12px] text-[var(--sub)]">{socialDesc}</p>
                  </div>
                </div>
                {socialPlatform === 'facebook' ? (
                  <>
                    <PaneField label="OG title" hint={aiButton('ogTitle')}>
                      <input
                        className={inputCls}
                        value={form.ogTitle ?? ''}
                        onChange={(e) => update({ ogTitle: e.target.value })}
                        placeholder={form.metaTitle || 'Inherits meta title when empty'}
                      />
                    </PaneField>
                    <PaneField label="OG description" hint={aiButton('ogDescription')}>
                      <textarea
                        className={`${inputCls} min-h-16 resize-y`}
                        value={form.ogDescription ?? ''}
                        onChange={(e) => update({ ogDescription: e.target.value })}
                        placeholder={form.metaDescription || 'Inherits meta description when empty'}
                      />
                    </PaneField>
                  </>
                ) : (
                  <>
                    <PaneField label="Twitter title">
                      <input
                        className={inputCls}
                        value={form.twitterTitle ?? ''}
                        onChange={(e) => update({ twitterTitle: e.target.value })}
                        placeholder={form.ogTitle || form.metaTitle || 'Inherits OG/meta title'}
                      />
                    </PaneField>
                    <PaneField label="Twitter description">
                      <textarea
                        className={`${inputCls} min-h-16 resize-y`}
                        value={form.twitterDescription ?? ''}
                        onChange={(e) => update({ twitterDescription: e.target.value })}
                        placeholder={
                          form.ogDescription ||
                          form.metaDescription ||
                          'Inherits OG/meta description'
                        }
                      />
                    </PaneField>
                  </>
                )}
                {alignmentRow('ogDescription')}
              </SeoAccordionItem>

              <SeoAccordionItem
                value="seo-analysis"
                title="SEO analysis"
                badge={
                  analysis ? (
                    <SeoAccordionBadge tone={problemCount > 0 ? 'bad' : 'good'}>
                      {problemCount > 0 ? `${problemCount} issues` : 'Looking good'}
                    </SeoAccordionBadge>
                  ) : undefined
                }
              >
                <SeoAnalysisList
                  checks={analysis?.checks ?? []}
                  onAiFixField={
                    isDraft ? undefined : (field) => void handleAiGenerate(field as AiField)
                  }
                  aiFixField={aiBusy}
                />
              </SeoAccordionItem>

              <SeoAccordionItem value="advanced" title="Advanced">
                <PaneField label="Canonical URL">
                  <input
                    className={inputCls}
                    value={form.canonicalUrl ?? ''}
                    onChange={(e) => update({ canonicalUrl: e.target.value })}
                    placeholder={displayUrl || 'https://example.com/page'}
                  />
                </PaneField>
                {!form.canonicalUrl?.trim() && displayUrl && !isDraft && (
                  <p className="mb-3 text-[11px] text-[var(--muted)]">
                    Auto-canonical: {record?.canonicalUrl ?? displayUrl}
                  </p>
                )}
                <PaneField label="Schema type">
                  <select
                    className={inputCls}
                    value={form.structuredDataType ?? 'WebPage'}
                    onChange={(e) => update({ structuredDataType: e.target.value })}
                  >
                    {SEO_SCHEMA_TYPES.map((t) => (
                      <option key={t} value={t}>
                        {t}
                      </option>
                    ))}
                  </select>
                </PaneField>
                <PaneToggleRow
                  label="Allow search engines to show this content?"
                  hint="When off, adds a noindex directive"
                  checked={!form.noindex}
                  onCheckedChange={(v) => update({ noindex: !v })}
                />
                <PaneToggleRow
                  label="Should search engines follow links?"
                  hint="When off, adds a nofollow directive"
                  checked={!form.nofollow}
                  onCheckedChange={(v) => update({ nofollow: !v })}
                />
                <div className="mt-2 border-t border-[var(--bdr)] pt-3">
                  <p className="mb-2 text-[11px] font-medium text-[var(--sub)]">
                    AI discovery (llms.txt)
                  </p>
                  <PaneField label="Include in llms.txt">
                    <select
                      className={inputCls}
                      value={form.llmsInclude ?? 'AUTO'}
                      onChange={(e) => update({ llmsInclude: e.target.value as LlmsInclude })}
                    >
                      <option value="AUTO">Auto — include when eligible</option>
                      <option value="ALWAYS">Always — pin it in</option>
                      <option value="NEVER">Never — exclude</option>
                    </select>
                  </PaneField>
                  <PaneField
                    label="Agent description"
                    hint={<CharCount value={form.llmsDescription ?? ''} max={LLMS_DESC_MAX} />}
                  >
                    <textarea
                      className={`${inputCls} min-h-16 resize-y`}
                      value={form.llmsDescription ?? ''}
                      onChange={(e) => update({ llmsDescription: e.target.value })}
                      placeholder={
                        form.keyTakeaway ||
                        form.metaDescription ||
                        'One line for an AI agent — what this page covers'
                      }
                    />
                  </PaneField>
                </div>
              </SeoAccordionItem>

              <SeoAccordionItem value="insights" title="Insights">
                <SeoInsightsMetrics readability={readability} plainText={plainText} />
              </SeoAccordionItem>

              <SeoAccordionItem value="linking-data" title="Linking data">
                <SeoLinkingSection
                  entityType={entityType}
                  entityId={entityId}
                  internalLinks={Number(internalLinks)}
                  externalLinks={Number(externalLinks)}
                  internalLinkOk={internalOk}
                  externalLinkOk={externalOk}
                />
              </SeoAccordionItem>
            </SeoAccordion>
          </>
        )}
      </InspectorPaneBody>

      <InspectorPaneFooter>
        <button
          type="button"
          onClick={() => void handleSave()}
          disabled={saving || loading || !!error}
          className="bg-primary text-primary-foreground w-full rounded-[7px] px-3 py-2 text-[13px] font-semibold transition-colors duration-[var(--motion-fast)] hover:opacity-90 focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
        >
          {saving ? 'Saving…' : isDraft ? 'Done' : 'Save Changes'}
        </button>
      </InspectorPaneFooter>
    </>
  )
}
