import { z } from 'zod'
import { type NoteCanvasData, emptyCanvas, parseCanvasData } from './canvas'

/**
 * Document-level model for project notes (KAR-662). A note is an A4 document
 * with one or more pages; each page is a `NoteCanvasData` (strokes). Documents
 * carry orientation, a page background (blank/grid/lines) and the grid/line
 * spacing in millimetres. Persisted in `project_notes.canvas_data` (jsonb);
 * legacy single-canvas notes are read as a one-page document.
 */

export const DOCUMENT_VERSION = 2

export type NoteOrientation = 'portrait' | 'landscape'
export type NoteBackground = 'blank' | 'grid' | 'lines'
export type NoteDocument = {
  version: number
  orientation: NoteOrientation
  background: NoteBackground
  gridMm: number
  pages: NoteCanvasData[]
}

export const ORIENTATIONS: ReadonlyArray<{ label: string; value: NoteOrientation }> = [
  { label: 'Hochformat', value: 'portrait' },
  { label: 'Querformat', value: 'landscape' },
]

export const BACKGROUNDS: ReadonlyArray<{ label: string; value: NoteBackground }> = [
  { label: 'Leer', value: 'blank' },
  { label: 'Kariert', value: 'grid' },
  { label: 'Liniert', value: 'lines' },
]

export const DEFAULT_GRID_MM = 5
const MIN_GRID_MM = 2
const MAX_GRID_MM = 50

const PX_PER_MM = 96 / 25.4
const A4_SHORT_MM = 210
const A4_LONG_MM = 297

/** Page size in CSS px (96 dpi) for the given orientation. */
export function pageDimensions(orientation: NoteOrientation): { width: number; height: number } {
  const short = Math.round(A4_SHORT_MM * PX_PER_MM)
  const long = Math.round(A4_LONG_MM * PX_PER_MM)
  return orientation === 'landscape'
    ? { width: long, height: short }
    : { width: short, height: long }
}

export function mmToPx(mm: number): number {
  return mm * PX_PER_MM
}

function clampGrid(mm: number): number {
  if (!Number.isFinite(mm)) return DEFAULT_GRID_MM
  return Math.min(MAX_GRID_MM, Math.max(MIN_GRID_MM, mm))
}

export function emptyDocument(): NoteDocument {
  return {
    version: DOCUMENT_VERSION,
    orientation: 'portrait',
    background: 'blank',
    gridMm: DEFAULT_GRID_MM,
    pages: [emptyCanvas()],
  }
}

const docSchema = z.object({
  version: z.number(),
  orientation: z.enum(['portrait', 'landscape']),
  background: z.enum(['blank', 'grid', 'lines']),
  gridMm: z.number(),
  pages: z.array(z.unknown()),
})

/** Coerce persisted/serialized input into a valid document (tolerant). */
export function parseDocument(value: unknown): NoteDocument {
  if (value == null) return emptyDocument()
  let candidate = value
  if (typeof candidate === 'string') {
    try {
      candidate = JSON.parse(candidate)
    } catch {
      return emptyDocument()
    }
  }

  const parsed = docSchema.safeParse(candidate)
  if (parsed.success) {
    const pages = parsed.data.pages.map(parseCanvasData)
    return {
      version: DOCUMENT_VERSION,
      orientation: parsed.data.orientation,
      background: parsed.data.background,
      gridMm: clampGrid(parsed.data.gridMm),
      pages: pages.length > 0 ? pages : [emptyCanvas()],
    }
  }

  // Legacy single-canvas note → wrap as a one-page portrait document.
  const looksLikeCanvas =
    typeof candidate === 'object' && candidate !== null && 'strokes' in candidate
  if (looksLikeCanvas) {
    return { ...emptyDocument(), pages: [parseCanvasData(candidate)] }
  }
  return emptyDocument()
}

// Light grey grid/line ink (palette "Border Subtle"); concrete hex so it also
// renders in the exported PDF, not just the themed UI.
const RULE_COLOR = '#C1C5CB'

/**
 * Inner SVG markup for the page background (pattern + filling rect). Empty for
 * 'blank'. Shared by the live canvas and the PDF rasteriser.
 */
export function backgroundSvg(
  background: NoteBackground,
  spacingPx: number,
  width: number,
  height: number,
): string {
  if (background === 'blank') return ''
  if (background === 'grid') {
    return (
      `<defs><pattern id="np-grid" width="${spacingPx}" height="${spacingPx}" patternUnits="userSpaceOnUse">` +
      `<path d="M ${spacingPx} 0 L 0 0 0 ${spacingPx}" fill="none" stroke="${RULE_COLOR}" stroke-width="1"/>` +
      `</pattern></defs><rect width="${width}" height="${height}" fill="url(#np-grid)"/>`
    )
  }
  return (
    `<defs><pattern id="np-lines" width="${width}" height="${spacingPx}" patternUnits="userSpaceOnUse">` +
    `<line x1="0" y1="${spacingPx}" x2="${width}" y2="${spacingPx}" stroke="${RULE_COLOR}" stroke-width="1"/>` +
    `</pattern></defs><rect width="${width}" height="${height}" fill="url(#np-lines)"/>`
  )
}
