import React from 'react'

const aspectRatios = {
  square: '1 / 1',
  portrait: '4 / 5',
  story: '9 / 16',
  landscape: '16 / 9',
}

const placementStyles = {
  'top-left': { top: '12%', left: '12%' },
  'top-right': { top: '12%', right: '12%' },
  'bottom-left': { bottom: '12%', left: '12%' },
  'bottom-right': { bottom: '12%', right: '12%' },
}

// ---------------------------------------------------------------------------
// DraggableElement — wraps any canvas element with pointer-drag positioning.
// Position is tracked locally during drag (avoids parent re-renders), then
// committed to the parent via onMove(elementId, x%, y%) on pointer-up.
// ---------------------------------------------------------------------------
function DraggableElement({ elementId, canvasRef, freePos, savedWidth, onMove, style, className, children }) {
  const dragState = React.useRef(null)
  const [localPos, setLocalPos] = React.useState(null)
  const [localWidth, setLocalWidth] = React.useState(null)

  function handlePointerDown(event) {
    event.preventDefault()
    event.stopPropagation()
    const canvas = canvasRef.current
    if (!canvas) return
    const canvasRect = canvas.getBoundingClientRect()
    const elemRect = event.currentTarget.getBoundingClientRect()
    const offsetX = event.clientX - elemRect.left
    const offsetY = event.clientY - elemRect.top
    const startX = ((elemRect.left - canvasRect.left) / canvasRect.width) * 100
    const startY = ((elemRect.top - canvasRect.top) / canvasRect.height) * 100
    // Capture element width as % of canvas so it stays the same after going absolute.
    const widthPct = Math.round((elemRect.width / canvasRect.width) * 1000) / 10
    dragState.current = { canvasRect, offsetX, offsetY, widthPct }
    event.currentTarget.setPointerCapture(event.pointerId)
    setLocalPos({ x: startX, y: startY })
    setLocalWidth(widthPct)
  }

  function calcPos(event) {
    const { canvasRect, offsetX, offsetY } = dragState.current
    return {
      x: Math.max(0, Math.min(94, ((event.clientX - canvasRect.left - offsetX) / canvasRect.width) * 100)),
      y: Math.max(0, Math.min(92, ((event.clientY - canvasRect.top - offsetY) / canvasRect.height) * 100)),
    }
  }

  function handlePointerMove(event) {
    if (!dragState.current) return
    setLocalPos(calcPos(event))
  }

  function handlePointerUp(event) {
    if (!dragState.current) return
    const pos = calcPos(event)
    const widthPct = dragState.current.widthPct
    dragState.current = null
    setLocalPos(null)
    setLocalWidth(null)
    onMove?.(elementId, Math.round(pos.x * 10) / 10, Math.round(pos.y * 10) / 10, widthPct)
  }

  const isDragging = localPos !== null
  const absPos = localPos ?? freePos

  // Width to use when the element is absolutely positioned.
  const resolvedWidth = localWidth ?? savedWidth

  const posStyle = absPos
    ? { position: 'absolute', left: `${absPos.x}%`, top: `${absPos.y}%`, zIndex: isDragging ? 50 : 10, width: resolvedWidth != null ? `${resolvedWidth}%` : undefined }
    : {}

  return (
    <div
      style={{ ...style, ...posStyle, cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none', userSelect: 'none' }}
      className={`${className} rounded ${isDragging ? 'ring-2 ring-sky-400/70 opacity-90' : 'ring-1 ring-transparent hover:ring-white/30'} transition-[box-shadow]`}
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerCancel={handlePointerUp}
    >
      {children}
    </div>
  )
}

function overlayStyle(style) {
  if (style === 'dark-strong') return 'linear-gradient(180deg, rgba(2,6,23,0.38), rgba(2,6,23,0.68))'
  if (style === 'light-soft') return 'linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.22))'
  if (style === 'dark-soft') return 'linear-gradient(180deg, rgba(2,6,23,0.18), rgba(2,6,23,0.48))'
  return 'none'
}

function positionStyle(position) {
  if (position === 'top') return { alignItems: 'flex-start', paddingTop: '10%' }
  if (position === 'upper-middle') return { alignItems: 'flex-start', paddingTop: '22%' }
  if (position === 'lower-middle') return { alignItems: 'flex-end', paddingBottom: '18%' }
  if (position === 'bottom') return { alignItems: 'flex-end', paddingBottom: '10%' }
  return { alignItems: 'center' }
}

function alignmentClass(alignment) {
  if (alignment === 'left') return 'justify-start items-start text-left'
  if (alignment === 'right') return 'justify-end items-end text-right'
  return 'justify-center items-center text-center'
}

function focalPositionStyle(position) {
  if (position === 'top') return 'center top'
  if (position === 'bottom') return 'center bottom'
  if (position === 'left') return 'left center'
  if (position === 'right') return 'right center'
  if (position === 'top-left') return 'left top'
  if (position === 'top-right') return 'right top'
  if (position === 'bottom-left') return 'left bottom'
  if (position === 'bottom-right') return 'right bottom'
  return 'center center'
}

function shadowValue(preset) {
  if (preset === 'none') return 'none'
  if (preset === 'strong') return '0 12px 36px rgba(2, 6, 23, 0.72)'
  return '0 8px 24px rgba(2, 6, 23, 0.5)'
}

function resolveTextBlocks(card, project) {
  const blocks = Array.isArray(project.text_blocks) ? project.text_blocks.filter((block) => block?.enabled !== false && String(block?.text || '').trim() !== '') : []
  if (blocks.length) return blocks

  const content = project.content || {}

  return [
    { key: 'title', type: 'title', text: card?.title || content.title || 'Untitled card', enabled: true },
    { key: 'quote', type: 'quote', text: card?.quote_text || content.quote_text || 'Your next quote starts here.', enabled: true },
    { key: 'author', type: 'author', text: card?.quote_author || content.quote_author || '', enabled: Boolean(card?.quote_author || content.quote_author) },
    { key: 'source', type: 'source', text: card?.quote_source || content.quote_source || '', enabled: Boolean(card?.quote_source || content.quote_source) },
  ].filter((block) => String(block.text || '').trim() !== '')
}

function blockClass(type) {
  if (type === 'title') return 'text-[11px] font-semibold uppercase tracking-[0.28em] text-white/55'
  if (type === 'author') return 'font-medium uppercase tracking-[0.18em] text-white/80 sm:text-sm lg:text-base'
  if (type === 'source') return 'text-[11px] uppercase tracking-[0.18em] text-white/50 sm:text-xs'
  if (type === 'caption') return 'text-[10px] uppercase tracking-[0.2em] text-white/45'
  if (type === 'body') return 'text-sm leading-6 text-white/90 sm:text-base'
  return 'font-semibold tracking-[-0.03em] sm:text-[1.65rem] lg:text-[2.1rem]'
}

function blockStyle(type, typography, textColor, accentColor, fontFamily) {
  const quoteSize = Math.max(10, Math.min(typography.quote_size || 72, 120))
  const authorSize = Math.max(14, Math.min(typography.author_size || 28, 42))
  const letterSpacing = Math.max(-1, Math.min(typography.letter_spacing || 0, 10))
  const lineHeight = Math.max(0.9, Math.min(typography.line_height || 1.2, 1.8))
  const shadowPreset = typography.shadow_preset || 'soft'
  // text_opacity: 10–100 (stored as integer percent), default 100
  const opacity = typography.text_opacity != null ? Math.max(10, Math.min(100, Number(typography.text_opacity))) / 100 : 1

  // Convert canvas pixels (1080-normalised) to container-query width units so
  // the preview proportions always match the rendered image regardless of how
  // wide the preview panel is.
  const toCqw = (px) => `${(px / 1080 * 100).toFixed(2)}cqw`

  const sizeMap = {
    title:   toCqw(Math.max(16, quoteSize * 0.48)),
    quote:   toCqw(quoteSize),
    author:  toCqw(authorSize),
    source:  toCqw(Math.max(12, authorSize * 0.82)),
    body:    toCqw(Math.max(16, quoteSize * 0.54)),
    caption: toCqw(Math.max(12, authorSize * 0.74)),
  }

  const fontSize = sizeMap[type] ?? sizeMap.quote

  const font = fontFamily ? { fontFamily } : {}

  if (type === 'title') {
    return { ...font, opacity, color: accentColor, fontSize, letterSpacing: `${Math.max(letterSpacing, 0) / 10}em`, textShadow: shadowValue(shadowPreset) }
  }

  if (type === 'author' || type === 'source') {
    return { ...font, opacity, color: accentColor, fontSize, textShadow: shadowValue(shadowPreset) }
  }

  if (type === 'body' || type === 'caption') {
    return { ...font, opacity, color: textColor, fontSize, lineHeight, textShadow: shadowValue(shadowPreset) }
  }

  return { ...font, opacity, color: textColor, fontSize, lineHeight, letterSpacing: `${letterSpacing / 12}px`, textShadow: shadowValue(shadowPreset) }
}

export default function NovaCardCanvasPreview({ card, fonts = [], className = '', editable = false, onElementMove = null, renderMode = false }) {
  const canvasRef = React.useRef(null)
  const project = card?.project_json || {}
  const layout = project.layout || {}
  const typography = project.typography || {}
  const resolvedFont = fonts.find((f) => f.key === (typography.font_preset || 'modern-sans'))
  const fontFamily = resolvedFont?.family || null
  const background = project.background || {}
  const backgroundImage = card?.background_image?.processed_url
  const colors = Array.isArray(background.gradient_colors) && background.gradient_colors.length >= 2
    ? background.gradient_colors
    : ['#0f172a', '#1d4ed8']
  const backgroundStyle = background.type === 'upload' && backgroundImage
    ? `url(${backgroundImage}) ${focalPositionStyle(background.focal_position)}/cover no-repeat`
    : background.type === 'solid'
      ? background.solid_color || '#111827'
      : `linear-gradient(180deg, ${colors[0]}, ${colors[1]})`

  const allTextBlocks = resolveTextBlocks(card, project)
  // Blocks with a saved free position are rendered absolutely; others stay in normal flow.
  const flowTextBlocks = allTextBlocks.filter((b) => b.pos_x == null || b.pos_y == null)
  const freeTextBlocks = allTextBlocks.filter((b) => b.pos_x != null && b.pos_y != null)

  const decorations = Array.isArray(project.decorations) ? project.decorations : []
  const assetItems = Array.isArray(project.assets?.items) ? project.assets.items : []
  const textColor = typography.text_color || '#ffffff'
  const accentColor = typography.accent_color || textColor
  const layoutMaxWidth = layout.max_width === 'compact' ? '62%' : layout.max_width === 'wide' ? '88%' : '76%'
  const maxWidth = typography.quote_width != null ? `${typography.quote_width}%` : layoutMaxWidth
  const padding = layout.padding === 'tight' ? '8%' : layout.padding === 'airy' ? '14%' : '11%'

  return (
    <div ref={canvasRef} data-card-canvas className={`relative overflow-hidden bg-slate-950 [container-type:inline-size] ${renderMode ? '' : 'rounded-[28px] border border-white/10 shadow-[0_30px_80px_rgba(2,6,23,0.35)]'} ${className}`} style={{ aspectRatio: aspectRatios[card?.format || 'square'] || aspectRatios.square }}>
      <div className="absolute inset-0" style={{ background: backgroundStyle, filter: background.type === 'upload' && Number(background.blur_level || 0) > 0 ? `blur(${Math.max(Number(background.blur_level) / 8, 0)}px)` : undefined }} />
      <div className="absolute inset-0" style={{ background: overlayStyle(background.overlay_style), opacity: Math.max(0, Math.min(Number(background.opacity || 50), 100)) / 100 }} />
      {!renderMode && (
        <div className="absolute left-4 top-4 rounded-full border border-white/10 bg-black/25 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-white/80 backdrop-blur">
          {(card?.format || 'square').replace('-', ' ')}
        </div>
      )}

      {/* Asset items (non-draggable) */}
      {assetItems.slice(0, 6).map((item, index) => {
        if (item?.type === 'frame') {
          const top = index % 2 === 0 ? '10%' : '88%'
          return <div key={`${item.asset_key || item.label || 'frame'}-${index}`} className="absolute left-[12%] right-[12%] h-px bg-white/35" style={{ top }} />
        }

        return (
          <div
            key={`${item.asset_key || item.label || 'asset'}-${index}`}
            className="absolute text-white/85 drop-shadow-[0_6px_14px_rgba(2,6,23,0.3)]"
            style={{
              top: `${12 + ((index % 3) * 18)}%`,
              left: `${10 + (Math.floor(index / 3) * 72)}%`,
              color: accentColor,
              fontSize: `${(Math.max(18, Math.min(item.size || 26, 56)) / 1080 * 100).toFixed(2)}cqw`,
            }}
          >
            {item.glyph || item.label || '✦'}
          </div>
        )
      })}

      {/* Flow-positioned text blocks */}
      <div className={`relative flex h-full w-full ${alignmentClass(layout.alignment)}`} style={{ padding, ...positionStyle(layout.position) }}>
        <div className="flex w-full flex-col gap-4" style={{ maxWidth }}>
          {flowTextBlocks.map((block, index) => {
            const type = block?.type || 'body'
            const text = type === 'author' ? `— ${block.text}` : block.text
            const defStyle = blockStyle(type, typography, textColor, accentColor, fontFamily)
            const blockCls = blockClass(type)
            const blockKey = `${block.key || type}-${index}`

            if (editable) {
              return (
                <DraggableElement
                  key={blockKey}
                  elementId={`block:${block.key || type}`}
                  canvasRef={canvasRef}
                  freePos={null}
                  onMove={onElementMove}
                  style={defStyle}
                  className={blockCls}
                >
                  {text}
                </DraggableElement>
              )
            }

            return (
              <div key={blockKey} style={defStyle} className={blockCls}>
                {text}
              </div>
            )
          })}
        </div>
      </div>

      {/* Absolutely-positioned text blocks (dragged out of flow) */}
      {freeTextBlocks.map((block, index) => {
        const type = block?.type || 'body'
        const text = type === 'author' ? `— ${block.text}` : block.text
        const defStyle = blockStyle(type, typography, textColor, accentColor, fontFamily)
        const blockCls = blockClass(type)
        const blockKey = `free-${block.key || type}-${index}`

        if (editable) {
          return (
            <DraggableElement
              key={blockKey}
              elementId={`block:${block.key || type}`}
              canvasRef={canvasRef}
              freePos={{ x: block.pos_x, y: block.pos_y }}
              savedWidth={block.pos_width ?? null}
              onMove={onElementMove}
              style={defStyle}
              className={blockCls}
            >
              {text}
            </DraggableElement>
          )
        }

        return (
          <div
            key={blockKey}
            style={{ ...defStyle, position: 'absolute', left: `${block.pos_x}%`, top: `${block.pos_y}%`, ...(block.pos_width != null ? { width: `${block.pos_width}%` } : {}) }}
            className={blockCls}
          >
            {text}
          </div>
        )
      })}

      {/* Decorations — rendered last so they sit on top of all text content */}
      {decorations.slice(0, 6).map((decoration, index) => {
        const hasFreePos = decoration.pos_x != null && decoration.pos_y != null
        const placementPos = placementStyles[decoration.placement] || placementStyles['top-right']
        const decOpacity = decoration.opacity != null ? Math.max(10, Math.min(100, Number(decoration.opacity))) / 100 : 0.85
        const decStyle = {
          color: accentColor,
          fontSize: `${(Math.max(18, Math.min(decoration.size || 28, 64)) / 1080 * 100).toFixed(2)}cqw`,
          opacity: decOpacity,
          zIndex: 20,
        }
        const decKey = `${decoration.key || decoration.glyph || 'dec'}-${index}`
        const decContent = decoration.glyph || '✦'

        if (editable) {
          const freePos = hasFreePos ? { x: decoration.pos_x, y: decoration.pos_y } : null
          const absStyle = hasFreePos
            ? { position: 'absolute', left: `${decoration.pos_x}%`, top: `${decoration.pos_y}%` }
            : { position: 'absolute', ...placementPos }
          return (
            <DraggableElement
              key={decKey}
              elementId={`decoration:${index}`}
              canvasRef={canvasRef}
              freePos={freePos}
              savedWidth={null}
              onMove={onElementMove}
              style={{ ...absStyle, ...decStyle }}
              className="drop-shadow-[0_6px_14px_rgba(2,6,23,0.3)]"
            >
              {decContent}
            </DraggableElement>
          )
        }

        return (
          <div
            key={decKey}
            className="absolute drop-shadow-[0_6px_14px_rgba(2,6,23,0.3)]"
            style={{ ...placementPos, ...decStyle }}
          >
            {decContent}
          </div>
        )
      })}
    </div>
  )
}
