'use client'

import { Puzzle } from 'lucide-react'
import type { PageSection } from '../../../lib/page-editor-service.js'
import { ErrorBoundary } from '../../../components/ErrorBoundary.js'
import { getSectionType } from '../section-types.js'
import { ADMIN_RENDER_OPTIONS, useAdminSectionRenderers } from './admin-renderers.js'
import { backgroundClass, paddingClass, type PostRenderContext } from './parts.js'
import { HeroBannerSection } from './HeroBannerSection.js'
import { MissionSection } from './MissionSection.js'
import { FeatureHighlightSection } from './FeatureHighlightSection.js'
import { ByTheNumbersSection } from './ByTheNumbersSection.js'
import { CtaBannerSection } from './CtaBannerSection.js'
import { ArticleBodySection } from './ArticleBodySection.js'
import { PullQuoteSection } from './PullQuoteSection.js'
import { AuthorBioSection } from './AuthorBioSection.js'
import { RelatedPostsSection } from './RelatedPostsSection.js'
import { GallerySection } from './GallerySection.js'
import { EmbedSection } from './EmbedSection.js'

/**
 * Section types with a real canvas body component below. Anything else —
 * config-declared custom types (`opal-*`), unmanaged sections, and the
 * `form` embed — renders as a structural placeholder, so the editors use
 * this set to tell authors the canvas shows structure, not design.
 */
const CANVAS_RENDERED_TYPES: ReadonlySet<string> = new Set([
  'hero',
  'text',
  'feature',
  'stats',
  'cta',
  'article-body',
  'quote',
  'author-bio',
  'related-posts',
  'gallery',
  'embed',
])

/** True when the canvas renders this type for real (not as a placeholder). */
export function hasCanvasRenderer(sectionType: string): boolean {
  return CANVAS_RENDERED_TYPES.has(sectionType)
}

/** Dispatch a section's body component by type. Shared by editor + preview. */
function SectionBody({ section, context }: { section: PageSection; context?: PostRenderContext }) {
  const customRenderers = useAdminSectionRenderers()

  switch (section.sectionType) {
    case 'hero':
      return <HeroBannerSection section={section} />
    case 'text':
      return <MissionSection section={section} />
    case 'feature':
      return <FeatureHighlightSection section={section} />
    case 'stats':
      return <ByTheNumbersSection section={section} />
    case 'cta':
      return <CtaBannerSection section={section} />
    case 'article-body':
      return <ArticleBodySection section={section} context={context} />
    case 'quote':
      return <PullQuoteSection section={section} />
    case 'author-bio':
      return <AuthorBioSection section={section} context={context} />
    case 'related-posts':
      return <RelatedPostsSection section={section} context={context} />
    case 'gallery':
      return <GallerySection section={section} />
    case 'embed':
      return <EmbedSection section={section} />
    default: {
      // Custom (config-declared) and unmanaged types: use a consumer-supplied
      // canvas renderer when one is registered (see admin-renderers.tsx).
      // Consumer code can throw, so a per-section error boundary falls back
      // to the structural placeholder instead of taking down the editor.
      const Custom = customRenderers[section.sectionType]
      if (Custom) {
        // The renderer contract types `settings` as an open record (matching
        // the sections-react wire shape); the admin's concrete
        // `SectionSettings` interface is the same object at runtime but has
        // no index signature, so bridge the nominal gap here.
        const wireSection = {
          ...section,
          settings: { ...section.settings } as Record<string, unknown>,
        }
        // Rendered as a component (not invoked inline) so a throw happens
        // inside the boundary's subtree and is actually caught.
        return (
          <ErrorBoundary key={section.id} fallback={<PlaceholderSection section={section} />}>
            <Custom section={wireSection} ctx={context} options={ADMIN_RENDER_OPTIONS} />
          </ErrorBoundary>
        )
      }
      // No renderer registered: a neutral placeholder keeps the canvas
      // reflecting the section's presence + order instead of a blank gap.
      return <PlaceholderSection section={section} />
    }
  }
}

/** Fallback canvas card for section types without a built-in renderer. */
function PlaceholderSection({ section }: { section: PageSection }) {
  const def = getSectionType(section.sectionType)
  const label = def?.name ?? section.name
  return (
    <div className="flex flex-col items-center justify-center gap-2 px-6 py-12 text-center">
      <span className="bg-muted text-muted-foreground flex h-10 w-10 items-center justify-center rounded-lg">
        <Puzzle className="h-5 w-5" aria-hidden />
      </span>
      <p className="text-foreground text-sm font-medium">{label}</p>
      <p className="text-muted-foreground text-xs">
        {section.unmanaged
          ? `Managed outside the editor (${section.sectionType}) — preview not available here.`
          : `Custom section "${section.sectionType}" — rendered by your site's renderer.`}
      </p>
    </div>
  )
}

export interface SectionRendererProps {
  section: PageSection
  /** Editor mode: adds click-to-select, the selection outline + label, and
   *  renders hidden sections muted instead of removing them. */
  editable?: boolean
  selected?: boolean
  onSelect?: (id: string) => void
  /** Lets the canvas register the DOM node for scroll-to-section. */
  registerRef?: (id: string, el: HTMLElement | null) => void
  /** Live post data for post-oriented section renderers (pages omit this). */
  context?: PostRenderContext
}

/**
 * Renders one section with its background + padding, dispatching to the
 * shared body component. In `editable` mode it layers editor-only chrome
 * (outline + a small purple type label) on top of the same markup the
 * public preview uses — so what you edit is what ships.
 */
export function SectionRenderer({
  section,
  editable = false,
  selected = false,
  onSelect,
  registerRef,
  context,
}: SectionRendererProps) {
  const settings = section.settings ?? {}
  const hidden = editable && !section.visible

  return (
    <section
      ref={(el) => registerRef?.(section.id, el)}
      data-section-id={section.id}
      id={settings.anchorId || undefined}
      className={`relative ${backgroundClass(settings)} ${paddingClass(settings)} ${
        settings.cssClass ?? ''
      } ${hidden ? 'opacity-40' : ''} ${
        editable ? 'cursor-pointer outline-2 -outline-offset-2 transition-[outline]' : ''
      } ${selected ? 'outline outline-violet-500' : 'outline-transparent'}`}
      onClick={editable ? () => onSelect?.(section.id) : undefined}
      // Selection is a convenience in the editor; the section list is the
      // primary, fully-accessible control surface, so this stays a region.
      aria-label={editable ? `${section.name} section` : undefined}
    >
      {editable && selected && (
        <span className="bg-brand pointer-events-none absolute top-0 left-0 z-10 rounded-br-md px-2 py-0.5 text-xs font-medium text-white">
          {section.name}
        </span>
      )}
      {editable && hidden && (
        <span className="bg-foreground pointer-events-none absolute top-0 right-0 z-10 rounded-bl-md px-2 py-0.5 text-xs font-medium text-white">
          Hidden
        </span>
      )}
      <SectionBody section={section} context={context} />
    </section>
  )
}
