
import React from 'react'
import { BaseBoxShapeTool, BaseBoxShapeUtil, Edge2d, HTMLContainer, ShapeUtil, T, TLBaseBoxShape, Vec, toDomPrecision } from '@tldraw/tldraw'
import type { TLHandle } from '@tldraw/tldraw'
import { PAGE_WIDTH } from '../../../utils/exam-canvas/model'
import type { ExamCanvasRegionKind, ExamCanvasShapeKind } from '../../../utils/exam-canvas/model'

export const PDF_PAGE_SHAPE_TYPE = 'exam-pdf-page'

export const SHAPE_TYPES = {
  boundary: 'exam-boundary',
  part: 'exam-part',
  response: 'exam-region-response',
  context: 'exam-region-context',
  question_number: 'exam-region-question-number',
  mark_area: 'exam-region-mark-area',
  reference: 'exam-region-reference',
  furniture: 'exam-region-furniture',
} as const

export type ExamPdfPageTLShape = TLBaseBoxShape & {
  type: typeof PDF_PAGE_SHAPE_TYPE
  props: { w: number; h: number; src: string; pageNumber: number }
}

export type ExamCanvasTLShape = TLBaseBoxShape & {
  type: typeof SHAPE_TYPES[keyof typeof SHAPE_TYPES]
  props: {
    w: number
    h: number
    label: string
    kind: ExamCanvasShapeKind
    maxMarks?: number
    responseForm?: string
    contextType?: string
    questionId?: string | null
    domainId?: string
  }
}

type CanvasPaletteEntry = {
  stroke: string
  fill: string
  darkStroke: string
  darkFill: string
  dash?: string
  label: string
  icon: string
  role: string
}

export const canvasShapePalette: Record<ExamCanvasShapeKind, CanvasPaletteEntry> = {
  boundary: { stroke: '#ef4444', fill: 'rgba(239,68,68,0.06)', darkStroke: '#f87171', darkFill: 'rgba(248,113,113,0.10)', dash: '8 6', label: 'Boundary', icon: '↕', role: 'start/end rule' },
  part: { stroke: '#f59e0b', fill: 'rgba(245,158,11,0.18)', darkStroke: '#fbbf24', darkFill: 'rgba(251,191,36,0.26)', label: 'Part', icon: '□', role: 'markable box' },
  response: { stroke: '#2563eb', fill: 'rgba(37,99,235,0.18)', darkStroke: '#60a5fa', darkFill: 'rgba(96,165,250,0.34)', label: 'Response', icon: '✎', role: 'student writing' },
  context: { stroke: '#7c3aed', fill: 'rgba(124,58,237,0.14)', darkStroke: '#a78bfa', darkFill: 'rgba(167,139,250,0.28)', dash: '6 5', label: 'Context', icon: '◉', role: 'stimulus' },
  question_number: { stroke: '#0f766e', fill: 'rgba(15,118,110,0.14)', darkStroke: '#2dd4bf', darkFill: 'rgba(45,212,191,0.24)', label: 'Question #', icon: '#', role: 'printed label' },
  mark_area: { stroke: '#16a34a', fill: 'rgba(22,163,74,0.14)', darkStroke: '#4ade80', darkFill: 'rgba(74,222,128,0.23)', label: 'Marks', icon: '[2]', role: 'printed marks' },
  reference: { stroke: '#0891b2', fill: 'rgba(8,145,178,0.14)', darkStroke: '#22d3ee', darkFill: 'rgba(34,211,238,0.24)', label: 'Reference', icon: '§', role: 'resource' },
  furniture: { stroke: '#64748b', fill: 'rgba(100,116,139,0.12)', darkStroke: '#cbd5e1', darkFill: 'rgba(148,163,184,0.18)', dash: '3 5', label: 'Furniture', icon: '×', role: 'ignore' },
}

const shapeCss = `
.exam-canvas-shape { --exam-stroke: var(--exam-light-stroke); --exam-fill: var(--exam-light-fill); }
[data-color-mode="dark"] .exam-canvas-shape, .tl-theme__dark .exam-canvas-shape { --exam-stroke: var(--exam-dark-stroke); --exam-fill: var(--exam-dark-fill); }
.exam-canvas-shape__pill { background: rgba(255,255,255,.90); color: var(--exam-stroke); box-shadow: 0 1px 4px rgba(15,23,42,.14); }
[data-color-mode="dark"] .exam-canvas-shape__pill, .tl-theme__dark .exam-canvas-shape__pill { background: rgba(15,23,42,.88); color: var(--exam-stroke); box-shadow: 0 1px 5px rgba(0,0,0,.35); }
`

function renderBoundaryLine(shape: ExamCanvasTLShape) {
  const p = canvasShapePalette.boundary
  const lineY = Math.max(1, Math.min(shape.props.h - 1, shape.props.h / 2))
  return (
    <HTMLContainer id={shape.id} style={{ width: toDomPrecision(shape.props.w), height: toDomPrecision(shape.props.h), overflow: 'visible', pointerEvents: 'all' }}>
      <style>{shapeCss}</style>
      <svg width={toDomPrecision(shape.props.w)} height={toDomPrecision(shape.props.h)} aria-label={`${p.label}: ${p.role}`} style={{ display: 'block', overflow: 'visible' }}>
        <line x1={0} x2={toDomPrecision(shape.props.w)} y1={lineY} y2={lineY} stroke="var(--exam-stroke)" strokeWidth={2.5} strokeDasharray={p.dash} strokeLinecap="round" style={{ '--exam-light-stroke': p.stroke, '--exam-dark-stroke': p.darkStroke } as React.CSSProperties} />
      </svg>
      <span className="exam-canvas-shape__pill" style={{ position: 'absolute', left: 8, top: -24, fontSize: 11, fontWeight: 900, textTransform: 'uppercase', letterSpacing: 0.6, borderRadius: 999, padding: '2px 7px', display: 'inline-flex', alignItems: 'center', gap: 5, color: p.stroke }}>
        <span aria-hidden="true">{p.icon}</span>
        {shape.props.label || p.label}
      </span>
    </HTMLContainer>
  )
}

function renderShape(shape: ExamCanvasTLShape) {
  const kind = shape.props.kind
  const p = canvasShapePalette[kind] ?? canvasShapePalette.response
  const isBoundary = kind === 'boundary'
  if (isBoundary) return renderBoundaryLine(shape)
  return (
    <HTMLContainer id={shape.id} style={{ width: toDomPrecision(shape.props.w), height: toDomPrecision(shape.props.h), pointerEvents: 'all' }}>
      <style>{shapeCss}</style>
      <div
        className={`exam-canvas-shape exam-canvas-shape--${kind}`}
        style={{
          '--exam-light-stroke': p.stroke,
          '--exam-light-fill': p.fill,
          '--exam-dark-stroke': p.darkStroke,
          '--exam-dark-fill': p.darkFill,
          width: '100%', height: '100%', boxSizing: 'border-box', border: `${isBoundary ? 2 : 1.5}px solid var(--exam-stroke)`,
          borderStyle: p.dash ? 'dashed' : 'solid', borderRadius: isBoundary ? 999 : 10,
          background: isBoundary ? 'transparent' : 'var(--exam-fill)', color: 'var(--exam-stroke)', fontFamily: 'Inter, system-ui, sans-serif',
          display: 'flex', alignItems: isBoundary ? 'center' : 'flex-start', justifyContent: isBoundary ? 'center' : 'space-between',
          padding: isBoundary ? '0 8px' : 8, boxShadow: isBoundary ? '0 0 0 3px rgba(239,68,68,0.08)' : '0 10px 22px rgba(15,23,42,0.10)', overflow: 'hidden', gap: 6,
        } as React.CSSProperties}
        aria-label={`${p.label}: ${p.role}`}
        title={`${p.label}: ${p.role}`}
      >
        <span className="exam-canvas-shape__pill" style={{ fontSize: 12, fontWeight: 900, textTransform: 'uppercase', letterSpacing: 0.6, borderRadius: 999, padding: '2px 7px', display: 'inline-flex', alignItems: 'center', gap: 5 }}>
          <span aria-hidden="true">{p.icon}</span>
          {shape.props.label || p.label}
        </span>
        {!isBoundary && shape.props.questionId && <span className="exam-canvas-shape__pill" style={{ fontSize: 11, fontWeight: 800, borderRadius: 999, padding: '2px 7px' }}>Attached</span>}
        {isBoundary && <span className="exam-canvas-shape__pill" style={{ fontSize: 10, fontWeight: 800, borderRadius: 999, padding: '1px 6px' }}>pair across pages</span>}
      </div>
    </HTMLContainer>
  )
}

function defaultProps(kind: ExamCanvasShapeKind, w: number, h: number) {
  const p = canvasShapePalette[kind]
  return { w, h, label: p.label, kind, responseForm: kind === 'response' ? 'lines' : undefined, contextType: kind === 'context' ? 'generic' : undefined }
}

const sharedProps = { w: T.number, h: T.number, label: T.string, kind: T.string, maxMarks: T.optional(T.number), responseForm: T.optional(T.string), contextType: T.optional(T.string), questionId: T.optional(T.string), domainId: T.optional(T.string) }
const ind = (s: ExamCanvasTLShape | ExamPdfPageTLShape) => <rect width={toDomPrecision(s.props.w)} height={toDomPrecision(s.props.h)} />

class PdfPageUtil extends BaseBoxShapeUtil<ExamPdfPageTLShape> {
  static override type = PDF_PAGE_SHAPE_TYPE
  static override props = { w: T.number, h: T.number, src: T.string, pageNumber: T.number }
  override getDefaultProps() { return { w: 780, h: 1100, src: '', pageNumber: 1 } }
  override canEdit() { return false }
  override component(shape: ExamPdfPageTLShape) {
    return (
      <HTMLContainer id={shape.id} style={{ width: toDomPrecision(shape.props.w), height: toDomPrecision(shape.props.h), pointerEvents: 'none' }}>
        <img src={shape.props.src} alt={'PDF page ' + shape.props.pageNumber} draggable={false} style={{ width: '100%', height: '100%', display: 'block', userSelect: 'none', pointerEvents: 'none', boxShadow: '0 2px 16px rgba(15,23,42,0.18)', background: '#fff' }} />
      </HTMLContainer>
    )
  }
  override indicator(shape: ExamPdfPageTLShape) { return ind(shape) }
}
class BoundaryUtil extends ShapeUtil<ExamCanvasTLShape> {
  static override type = SHAPE_TYPES.boundary
  static override props = sharedProps

  override getDefaultProps() { return defaultProps('boundary', PAGE_WIDTH, 8) }
  override canEdit() { return false }
  override canResize() { return false }
  override canBind() { return false }
  override hideResizeHandles() { return true }
  override hideRotateHandle() { return true }
  override hideSelectionBoundsBg() { return true }

  private pageSpanForY(y: number) {
    const pages = this.editor.getCurrentPageShapes().filter((shape): shape is ExamPdfPageTLShape => shape.type === PDF_PAGE_SHAPE_TYPE)
    const hit = pages.find((page) => y >= page.y && y <= page.y + page.props.h)
    const nearest = hit ?? pages.reduce<ExamPdfPageTLShape | null>((best, page) => {
      if (!best) return page
      const pageDy = Math.min(Math.abs(y - page.y), Math.abs(y - (page.y + page.props.h)))
      const bestDy = Math.min(Math.abs(y - best.y), Math.abs(y - (best.y + best.props.h)))
      return pageDy < bestDy ? page : best
    }, null)
    return nearest ? { x: nearest.x, w: nearest.props.w } : { x: 0, w: PAGE_WIDTH }
  }

  private normalize(shape: ExamCanvasTLShape): ExamCanvasTLShape {
    const span = this.pageSpanForY(shape.y + shape.props.h / 2)
    return { ...shape, x: span.x, rotation: 0, props: { ...shape.props, w: span.w, h: 8, kind: 'boundary' } }
  }

  override getGeometry(shape: ExamCanvasTLShape) {
    const y = shape.props.h / 2
    return new Edge2d({ start: new Vec(0, y), end: new Vec(shape.props.w, y) })
  }

  override getHandles(shape: ExamCanvasTLShape): TLHandle[] {
    return [{ id: 'y', type: 'vertex', index: 'a1' as any, x: shape.props.w / 2, y: shape.props.h / 2, canSnap: false }]
  }

  override onBeforeCreate(next: ExamCanvasTLShape) { return this.normalize(next) }
  override onBeforeUpdate(_prev: ExamCanvasTLShape, next: ExamCanvasTLShape) { return this.normalize(next) }
  override onTranslate(initial: ExamCanvasTLShape, current: ExamCanvasTLShape): any {
    return this.normalize({ ...current, x: initial.x })
  }
  override onHandleDrag(shape: ExamCanvasTLShape, { handle }: { handle: TLHandle }): any {
    return this.normalize({ ...shape, y: shape.y + handle.y - shape.props.h / 2 })
  }
  override component(shape: ExamCanvasTLShape) { return renderShape(shape) }
  override indicator(shape: ExamCanvasTLShape) { return <path d={`M 0 ${toDomPrecision(shape.props.h / 2)} L ${toDomPrecision(shape.props.w)} ${toDomPrecision(shape.props.h / 2)}`} /> }
}
class PartUtil extends BaseBoxShapeUtil<ExamCanvasTLShape> { static override type = SHAPE_TYPES.part; static override props = sharedProps; override getDefaultProps(){ return defaultProps('part', 420, 170) }; override component(shape: ExamCanvasTLShape){ return renderShape(shape) }; override indicator(shape: ExamCanvasTLShape){ return ind(shape) } }
function regionUtil(type: string, kind: ExamCanvasRegionKind, w = 360, h = 120) { return class extends BaseBoxShapeUtil<ExamCanvasTLShape> { static override type = type; static override props = sharedProps; override getDefaultProps(){ return defaultProps(kind, w, h) }; override component(shape: ExamCanvasTLShape){ return renderShape(shape) }; override indicator(shape: ExamCanvasTLShape){ return ind(shape) } } }

class BoundaryTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.boundary; static override initial = 'pointing'; shapeType = SHAPE_TYPES.boundary }
class PartTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.part; static override initial = 'pointing'; shapeType = SHAPE_TYPES.part }
class ResponseTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.response; static override initial = 'pointing'; shapeType = SHAPE_TYPES.response }
class ContextTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.context; static override initial = 'pointing'; shapeType = SHAPE_TYPES.context }
class QuestionNumberTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.question_number; static override initial = 'pointing'; shapeType = SHAPE_TYPES.question_number }
class MarkAreaTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.mark_area; static override initial = 'pointing'; shapeType = SHAPE_TYPES.mark_area }
class ReferenceTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.reference; static override initial = 'pointing'; shapeType = SHAPE_TYPES.reference }
class FurnitureTool extends BaseBoxShapeTool { static override id = SHAPE_TYPES.furniture; static override initial = 'pointing'; shapeType = SHAPE_TYPES.furniture }

export const examCanvasShapeUtils = [PdfPageUtil, BoundaryUtil, PartUtil, regionUtil(SHAPE_TYPES.response, 'response'), regionUtil(SHAPE_TYPES.context, 'context'), regionUtil(SHAPE_TYPES.question_number, 'question_number', 170, 80), regionUtil(SHAPE_TYPES.mark_area, 'mark_area', 170, 80), regionUtil(SHAPE_TYPES.reference, 'reference'), regionUtil(SHAPE_TYPES.furniture, 'furniture')] as const
export const examCanvasTools = [BoundaryTool, PartTool, ResponseTool, ContextTool, QuestionNumberTool, MarkAreaTool, ReferenceTool, FurnitureTool] as const

export function isPdfPageShape(type: string): boolean {
  return type === PDF_PAGE_SHAPE_TYPE
}

export function shapeTypeToKind(type: string): ExamCanvasShapeKind | null {
  const entry = Object.entries(SHAPE_TYPES).find(([, v]) => v === type)
  return (entry?.[0] as ExamCanvasShapeKind | undefined) ?? null
}
