// Core node types for Jalpi Page Builder page structure

export type ElementType =
  | 'section'
  | 'container'
  | 'column'
  | 'heading'
  | 'text'
  | 'image'
  | 'button'
  | 'spacer'
  | 'divider'
  | 'video'
  | 'maps'
  | 'html'
  | 'icon'
  | 'shortcode'
  | 'logo'
  | 'form'
  | 'alert'
  | 'counter'
  | 'progress'
  | 'testimonial'
  | 'accordion'
  | 'badge'
  | 'icon-list'
  | 'pricing-table'
  | 'team-member'
  | 'faq'
  | 'table'
  | 'feature-card'
  | 'cta'
  | 'stats-card'
  | 'timeline'
  | 'testimonials'
  | 'tabs'
  | 'tabs-adv'
  | 'menu'
  | 'adv-menu'
  | 'breadcrumbs'
  | 'image-box'
  | 'social-icons'
  | 'gallery'
  | 'image-carousel'
  | 'countdown'
  | 'flip-box'
  | 'star-rating'
  // Registry-driven widgets (rendered via the generic path — see
  // src/widgets/registry.ts, GenericRenderer.tsx, GenericContent.tsx and the
  // PHP generic element/sanitizer default branches). New schema-only widgets
  // are added to this union as they're introduced.
  // Batch: self-contained schema widgets (Effects)
  | 'marquee'
  | 'typewriter-text'
  | 'shape-divider'
  // Social widgets
  | 'social-share'
  | 'whatsapp-button'
  | 'youtube-gallery'
  | 'facebook-feed'
  | 'instagram-feed'
  // Icon widget (demonstrates the full icon-library picker in a schema widget)
  | 'icon-box'
  // WooCommerce — bespoke (server-side WC_Query + AJAX pagination, so it does
  // NOT flow through the generic schema path).
  | 'products-grid'
  | 'product-categories'
  | 'single-product';

export interface StyleMap {
  fontSize?: string;
  fontWeight?: string;
  fontFamily?: string;
  color?: string;
  backgroundColor?: string;
  textAlign?: string;
  lineHeight?: string;
  letterSpacing?: string;
  marginTop?: string;
  marginBottom?: string;
  marginLeft?: string;
  marginRight?: string;
  paddingTop?: string;
  paddingBottom?: string;
  paddingLeft?: string;
  paddingRight?: string;
  width?: string;
  height?: string;
  minHeight?: string;
  borderRadius?: string;
  border?: string;
  boxShadow?: string;
  opacity?: string;
  display?: string;
  textDecoration?: string;
  textTransform?: string;
  gap?: string;
  rowGap?: string;
  columnGap?: string;
  linkColor?: string;
  colorHover?: string;
  linkColorHover?: string;
  paragraphSpacing?: string;
  position?: string;
  top?: string;
  right?: string;
  bottom?: string;
  left?: string;
  zIndex?: string;
  [key: string]: string | undefined;
}

export interface Background {
  type: 'color' | 'image' | 'gradient';
  value?: string;
  url?: string;
  position?: string;
  size?: string;
}

export interface Padding {
  top: string;
  right: string;
  bottom: string;
  left: string;
}

export interface BgData {
  type?: 'classic' | 'gradient' | 'video' | 'slideshow';
  color?: string;
  imageUrl?: string;
  imagePosition?: string;
  imageSize?: string;
  gradient?: string;
  gradientColor1?: string;
  gradientLocation1?: number;
  gradientColor2?: string;
  gradientLocation2?: number;
  gradientType?: 'linear' | 'radial';
  gradientAngle?: number;
  videoUrl?: string;
  opacity?: string;
}

export interface BorderData {
  borderType?: string;
  borderColor?: string;
  borderWidth?: string;
  borderRadius?: string;
  boxShadow?: string;
}

export interface SectionSettings {
  background?: Background;
  padding?: Padding;
  minHeight?: string;
  fullWidth?: boolean;
  contentWidth?: 'full' | 'boxed';
  boxedWidth?: string;
  alignItems?: string;
  // Position
  position?: string;
  posTop?: string;
  posRight?: string;
  posBottom?: string;
  posLeft?: string;
  // Background / border / overlay / CSS
  bgNormal?: BgData;
  bgHover?: BgData;
  overlayNormal?: BgData;
  overlayHover?: BgData;
  borderNormal?: BorderData;
  borderHover?: BorderData;
  cssId?: string;
  cssClass?: string;
  /** Attached reusable-class keys (Class-Based Styling System), in attach order. */
  cssClasses?: string[];
  /** Raw per-element CSS; `selector` is replaced server-side with this node's own scoped selector. */
  customCss?: string;
}

export interface ColumnSettings {
  width?: string;
  alignSelf?: string;
  bgNormal?: BgData;
  bgHover?: BgData;
  overlayNormal?: BgData;
  overlayHover?: BgData;
  borderNormal?: BorderData;
  borderHover?: BorderData;
  cssId?: string;
  cssClass?: string;
  cssClasses?: string[];
  customCss?: string;
}

export interface ContainerSettings {
  contentWidth?: 'full' | 'boxed';
  boxedWidth?: string;
  alignItems?: string;
  columnGap?: string;
  // Layout mode
  layoutMode?: 'block' | 'flex' | 'grid';
  // Flex
  flexDirection?: string;
  flexWrapMode?: string;
  flexJustify?: string;
  flexAlign?: string;
  flexGap?: string;
  // Grid
  gridAutoMode?: 'none' | 'fill' | 'fit';
  gridColumns?: string;
  gridRows?: string;
  gridGap?: string;
  gridMinColSize?: string;
  // Position
  position?: string;
  posTop?: string;
  posRight?: string;
  posBottom?: string;
  posLeft?: string;
  // Background / border / overlay / CSS
  bgNormal?: BgData;
  bgHover?: BgData;
  overlayNormal?: BgData;
  overlayHover?: BgData;
  borderNormal?: BorderData;
  borderHover?: BorderData;
  cssId?: string;
  cssClass?: string;
  cssClasses?: string[];
  customCss?: string;
  /* kept for backwards-compat with existing saved pages */
  columns?: number;
  gap?: string;
  direction?: string;
  justifyContent?: string;
  flexWrap?: string;
}

export interface HeadingContent {
  text: string;
  tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
}

export interface TextContent {
  html?: string;
  text?: string;
}

export interface ImageContent {
  url?: string;
  alt?: string;
  linkUrl?: string;
  newTab?: boolean;
}

export interface ButtonContent {
  text: string;
  url?: string;
  newTab?: boolean;
}

export interface VideoContent {
  url?: string;
  controls?: boolean;
  autoplay?: boolean;
  muted?: boolean;
  loop?: boolean;
}

export interface MapsContent {
  address?: string;
  height?: string;
}

export interface HtmlContent {
  html?: string;
}

// Stored icon value — supports builtin SVGs, CDN font icons, and image URLs.
// Legacy format: icon is a plain string key (e.g. 'star') — auto-upgraded on render.
export interface IconValue {
  type: 'svg' | 'font' | 'img';
  lib: string;   // 'builtin' | 'fa-solid' | 'fa-regular' | 'fa-brands' | 'bootstrap' | 'material' | 'tabler' | 'custom'
  name: string;  // icon identifier
  svg?: string;  // full SVG string (cached at pick time for svg type)
  cls?: string;  // CSS class string for font icons (e.g. 'fa-solid fa-house')
  url?: string;  // image URL for img type
  alt?: string;  // alt text for img type
}

export interface IconContent {
  icon?: string | IconValue;
  // Visual properties now live in node.styles (style tab); kept here only for legacy compat
  view?: 'default' | 'stacked' | 'framed';
  borderRadius?: string;
  link?: string;
  newTab?: boolean;
}

export interface ShortcodeContent {
  shortcode?: string;
}

export interface LogoContent {
  url?: string;
  alt?: string;
  width?: string;
  linkUrl?: string;
  newTab?: boolean;
}

export interface FormContent {
  plugin?: string;
  form_id?: string;
}

export type NodeContent =
  | HeadingContent
  | TextContent
  | ImageContent
  | ButtonContent
  | VideoContent
  | MapsContent
  | HtmlContent
  | IconContent
  | ShortcodeContent
  | LogoContent
  | FormContent;

export interface CBNode {
  id: string;
  type: ElementType;
  content?: NodeContent;
  styles?: StyleMap;
  settings?: SectionSettings | ContainerSettings | Record<string, unknown>;
  children?: CBNode[];
  // Responsive overrides
  tabletStyles?: StyleMap;
  mobileStyles?: StyleMap;
  // Per-device visibility
  hideOn?: { desktop?: boolean; tablet?: boolean; mobile?: boolean };
  // Layer lock — prevents accidental edits in the canvas
  locked?: boolean;
  // Global style inheritance — typographyId/buttonId link to composite presets
  // (GlobalStyles.typography/buttons); tokens links individual style properties
  // to atomic Design System tokens (DesignSystem categories below). A literal
  // value already present in `styles`/`tabletStyles`/`mobileStyles` for the
  // same property always overrides either kind of link.
  globalStyleRef?: {
    typographyId?: string;
    buttonId?: string;
    tokens?: Record<string, { category: TokenCategory; id: string }>;
  };
  // Interaction Engine — entrance/scroll/hover/click/etc. animation rules.
  interactions?: InteractionRule[];
}

// ── Interaction Engine ───────────────────────────────────────────────────────
// Extends the (previously FAQ-only) Animation panel into a generic engine
// available to every node type. See includes/class-jalpi-sanitizer.php's
// sanitize_interactions() (server-side validation), includes/class-jalpi-
// renderer.php's apply_interactions()/maybe_add_interaction_css() (how a rule
// becomes a data-jalpi-interact attribute + scoped CSS), and assets/js/
// jalpi-frontend.js's initInteractions() (the runtime that drives it).

export type InteractionTrigger =
  | 'pageLoad' | 'hover' | 'parentHover' | 'mouseEnter' | 'mouseLeave' | 'click' | 'doubleClick'
  | 'scrollIntoView' | 'scrollProgress' | 'mouseMove' | 'focus' | 'blur';

export type InteractionAction =
  | 'fade' | 'slide' | 'scale' | 'rotate' | 'translate' | 'opacity' | 'blur'
  | 'skew' | 'flip' | 'bounce' | 'zoom' | 'customTransform' | 'jackInTheBox'
  // Animation-library extension: 'elastic' mirrors 'bounce' (direction-aware,
  // resolves to the 'elasticX' keyframe for left/right — see class-jalpi-
  // renderer.php's add_interaction_keyframe_css()). 'reveal'/'wipe' animate
  // clip-path (+ opacity for 'reveal', matching the existing slide-has-
  // opacity/translate-doesn't split) via the 'clipShape' setting below.
  | 'elastic' | 'reveal' | 'wipe';

/**
 * One flexible settings bag (all optional) rather than a discriminated union
 * per trigger×action combination — the UI shows/hides field groups based on
 * the rule's trigger/action, but the underlying shape stays uniform, matching
 * this codebase's existing loose `settings: Record<string,unknown>` convention.
 */
export interface InteractionSettings {
  // Advanced controls — generic, shown for every rule.
  duration?: string;
  delay?: string;
  easing?: string;
  repeat?: string; // numeric string, or 'infinite'
  infinite?: boolean;
  direction?: string; // slide direction (up/down/left/right) OR animation-direction (normal/reverse/alternate/alternate-reverse)
  distance?: string;
  rotationDeg?: string;
  scaleValue?: string;
  blurAmount?: string;
  opacityValue?: string;
  transformOrigin?: string;
  customTransform?: string;
  /** Companion to customTransform for the 'customTransform' action — an
   * optional CSS filter() value animated alongside the transform (used by
   * the directional Blur presets, since the dedicated 'blur' action's own
   * keyframe has no translate component). */
  customFilter?: string;
  /** clip-path shape for the 'reveal'/'wipe' actions. 'edge' (default) uses
   * `direction` for which side it reveals from; the other three reveal from
   * the center outward regardless of `direction`. */
  clipShape?: 'edge' | 'circle' | 'diamond' | 'square';
  // Scroll-specific (trigger === 'scrollIntoView' | 'scrollProgress')
  startOffset?: string;
  endOffset?: string;
  viewportPct?: string;
  /** @deprecated never read at runtime — `replay` is the single source of truth. Kept only so old saved rules still round-trip. */
  onceOnly?: boolean;
  replay?: boolean;
  scrollDirection?: 'vertical' | 'horizontal' | 'both';
  // Hover-specific (trigger === 'hover' | 'parentHover')
  hoverInAction?: InteractionAction;
  hoverOutAction?: InteractionAction;
  transitionDuration?: string;
  /** "Peek on hover": element is hidden at rest, plays its reveal on each
   * hover, then automatically hides itself again once that playback
   * finishes — rather than staying visible until mouseleave. */
  hideAfterPlay?: boolean;
  // Mouse-move-specific (trigger === 'mouseMove')
  moveX?: string;
  moveY?: string;
  strength?: string;
  reverse?: boolean;
  smoothness?: string;
}

export interface InteractionRule {
  id: string;
  trigger: InteractionTrigger;
  action: InteractionAction;
  enabled?: boolean;
  settings: InteractionSettings;
  /** Per-breakpoint enable/disable. Absent tier = enabled on that breakpoint. */
  responsive?: { desktop?: boolean; tablet?: boolean; mobile?: boolean };
}

// ── Global Styles ─────────────────────────────────────────────────────────────

export interface GlobalColor {
  id:    string;
  name:  string;
  value: string;
}

export interface TypographyPreset {
  id:            string;
  name:          string;
  fontFamily?:   string;
  fontSize?:     string;
  fontWeight?:   string;
  lineHeight?:   string;
  letterSpacing?: string;
  textTransform?: string;
  color?:        string;
}

export interface ButtonPreset {
  id:               string;
  name:             string;
  backgroundColor?: string;
  color?:           string;
  borderRadius?:    string;
  paddingTop?:      string;
  paddingBottom?:   string;
  paddingLeft?:     string;
  paddingRight?:    string;
  fontSize?:        string;
  fontWeight?:      string;
  fontFamily?:      string;
  border?:          string;
  boxShadow?:       string;
  textTransform?:   string;
}

export interface SpacingScale {
  xs:   string;
  sm:   string;
  md:   string;
  lg:   string;
  xl:   string;
  '2xl': string;
}

export interface GlobalStyles {
  colors:     GlobalColor[];
  typography: TypographyPreset[];
  buttons:    ButtonPreset[];
  spacing:    SpacingScale;
}

export interface PageData {
  version: string;
  sections: CBNode[];
  globalStyles?: GlobalStyles;
}

// ── Class-Based Styling System ──────────────────────────────────────────────
// Reusable, site-wide CSS classes (Bricks-style). Structurally a headless
// node — styles/tabletStyles/mobileStyles + the same bg/border settings
// bucket nodes use — so it's compiled to CSS with the exact same renderer
// helpers as node styles. globalStyleRef lets a class itself inherit from the
// site-wide Design System, same link shape/resolution as CBNode.globalStyleRef.
export interface JalpiClass {
  key: string;
  name: string;
  styles: StyleMap;
  tabletStyles?: StyleMap;
  mobileStyles?: StyleMap;
  settings?: {
    bgNormal?: BgData;
    bgHover?: BgData;
    overlayNormal?: BgData;
    overlayHover?: BgData;
    borderNormal?: BorderData;
    borderHover?: BorderData;
  };
  globalStyleRef?: CBNode['globalStyleRef'];
  favorite: boolean;
  sortOrder: number;
  updatedAt?: string;
}

// ── Design System (site-wide) ──────────────────────────────────────────────
// Superseded the per-page GlobalStyles above as the real "inherit from
// global, override locally" source of truth — GlobalStyles/PageData.globalStyles
// are kept unchanged for backward compatibility with already-saved pages.

/** One generic Design System token, reused for all 8 atomic categories below. */
export interface DesignToken {
  id: string;
  name: string;
  value: string;
  /** Responsive overrides — same {tabletValue,mobileValue} sibling-value pattern as node styles. */
  tabletValue?: string;
  mobileValue?: string;
}

export type TokenCategory =
  | 'colors'
  | 'fontFamilies'
  | 'fontSizes'
  | 'lineHeights'
  | 'letterSpacing'
  | 'borderRadius'
  | 'boxShadows'
  | 'spacing'
  | 'containerWidths'
  | 'opacity';

export interface DesignSystem {
  colors: DesignToken[];
  fontFamilies: DesignToken[];
  fontSizes: DesignToken[];
  lineHeights: DesignToken[];
  letterSpacing: DesignToken[];
  borderRadius: DesignToken[];
  boxShadows: DesignToken[];
  spacing: DesignToken[];
  containerWidths: DesignToken[];
  opacity: DesignToken[];
  /** Composite presets, unchanged shape — reused as-is from the legacy GlobalStyles model. */
  typography: TypographyPreset[];
  buttons: ButtonPreset[];
}

// Editor-specific types

export type Breakpoint = 'desktop' | 'tablet' | 'mobile';

export interface HistoryEntry {
  pageData: PageData;
  timestamp: number;
  label?: string;
}

export interface EditorState {
  postId: number;
  pageData: PageData;
  selectedId: string | null;
  hoveredId: string | null;
  breakpoint: Breakpoint;
  isDirty: boolean;
  isSaving: boolean;
  isPublishing: boolean;
  history: HistoryEntry[];
  historyIndex: number;
  panelTab: 'properties' | 'style' | 'animation' | 'responsive';
  showLayers: boolean;
  zoom: number;
  clipboard: CBNode | null;
  styleClipboard: StyleMap | null;
  selectionSeq: number;
  // Site-wide Design System — separate save lifecycle from page data (see editorStore.ts).
  designSystem: DesignSystem;
  designSystemDirty: boolean;
  designSystemSaving: boolean;
  // Class-Based Styling System — reusable classes, loaded once, own save lifecycle (see editorStore.ts).
  classes: JalpiClass[];
  classesLoaded: boolean;
  /** null = Style/Advanced tabs edit the selected node; a class key = they edit that class instead. */
  editingClassKey: string | null;
}

export interface EditorConfig {
  postId: number;
  apiUrl: string;
  nonce: string;
  adminUrl: string;
  homeUrl: string;
  assetsUrl: string;
  version: string;
  colors: Array<{ name: string; color: string; slug: string }>;
  fonts: Array<{ label: string; value: string }>;
  designSystem?: DesignSystem;
  canManageDesignSystem?: boolean;
}

// Component block definitions
export interface BlockDef {
  type: ElementType;
  label: string;
  icon: string;
  defaultNode: Partial<CBNode>;
}

// Template library
export interface CbTemplate {
  /**
   * Saved templates use the numeric post id. Starter-Site kit templates are
   * virtual and use a string id of the form `kit:<kit>:<slug>`, so this is a
   * union — compare with String(id) when matching.
   */
  id: number | string;
  name: string;
  description: string;
  type: 'page' | 'section' | 'element';
  category: string;
  category_label: string;
  thumbnail: string;
  created: string;
  modified: string;
  bundled_slug?: string | null;
  /** Free-text tags for filtering. */
  tags?: string[];
  /** 'user' for saved templates, 'kit' for read-only Starter Site pages. */
  source?: 'user' | 'kit';
  /** Kit id when source === 'kit'. */
  kit?: string;
  /** Kit templates cannot be renamed, edited or deleted. */
  readonly?: boolean;
}

/** Paginated envelope returned by GET /templates. */
export interface CbTemplateList {
  items: CbTemplate[];
  total: number;
  page: number;
  per_page: number;
  pages: number;
}

export interface CbTemplateCategory {
  slug: string;
  label: string;
  count: number;
}

export interface CbTemplateCategories {
  categories: CbTemplateCategory[];
  total: number;
  tags: string[];
}

export interface CbTemplateWithData extends CbTemplate {
  data: PageData | CBNode;
}

export interface TemplateCategory {
  slug: string;
  label: string;
  count: number;
}

// Revision system
export interface CbRevision {
  id: number;
  post_id: number;
  user_id: number;
  user_name: string;
  label: string;
  is_snapshot: boolean;
  created_at: string;
}

export interface CbRevisionWithData extends CbRevision {
  data: PageData;
}
