import React, { CSSProperties } from 'react';
import { useNode, UserComponent } from '@craftjs/core';
import { cssPropsToString } from '../../utils/style-helpers';
import { escapeAttr } from '../../utils/escape';

// The only tag names Container actually supports (matches the TS union
// below and the `tag` default in `.craft.props`). `tag` is settable via the
// AI `update_props` path and from deserialized saved state -- neither is
// type-checked at runtime -- so a malicious value like
// `div><img src=x onerror=alert(1)` must never reach the `<${tag}` template
// position in `toHtml`/the live render. Anything not in this allowlist
// falls back to `'div'`.
const ALLOWED_CONTAINER_TAGS = ['div', 'section', 'article', 'header', 'footer', 'main'] as const;
export type ContainerTag = (typeof ALLOWED_CONTAINER_TAGS)[number];

export const sanitizeContainerTag = (tag: unknown): ContainerTag =>
  (ALLOWED_CONTAINER_TAGS as readonly unknown[]).includes(tag) ? (tag as ContainerTag) : 'div';

interface ContainerProps {
  style?: CSSProperties;
  tag?: 'div' | 'section' | 'article' | 'header' | 'footer' | 'main';
  children?: React.ReactNode;
  cssId?: string;
  cssClass?: string;
  anchorId?: string;
  hideOnDesktop?: boolean;
  hideOnTablet?: boolean;
  hideOnMobile?: boolean;
  animation?: string;
  animationDelay?: string;
  fullWidth?: boolean;
  contentWidth?: 'boxed' | 'full';
}

// Map textAlign to a flex alignItems value so block-level children (images,
// columns, sections) align horizontally — textAlign alone only affects inline
// content. Returns undefined when no alignment is set so we leave layout as
// normal block flow.
const flexAlignFromTextAlign = (textAlign: CSSProperties['textAlign']): CSSProperties => {
  if (textAlign === 'center') return { display: 'flex', flexDirection: 'column', alignItems: 'center' };
  if (textAlign === 'right') return { display: 'flex', flexDirection: 'column', alignItems: 'flex-end' };
  if (textAlign === 'left') return { display: 'flex', flexDirection: 'column', alignItems: 'flex-start' };
  return {};
};

// Container only becomes display:flex/flex-direction:column at its root
// (both in the editor render below and in toHtml) when the user has
// actually set `style.justifyContent` (the Vertical Alignment control,
// paired with `style.minHeight`) -- i.e. the flex conversion is gated on
// vertical-align actually being in use, not unconditional. In-flow children
// of a flex container get CSS-blockified, which would force components that
// deliberately render `display:inline-block` (ButtonLink, Icon) to stack
// vertically instead of sitting side-by-side -- a real visual regression for
// any container/section that never touches vertical alignment, not a no-op.
// So plain block flow (no `display`/`flex-direction` at all) is preserved
// unless vertical-align is set. `flexAlignFromTextAlign` above still
// supplies its own conditional flex conversion (cross-axis alignItems from
// `textAlign`) independently -- unrelated to this gate.

export const Container: UserComponent<ContainerProps> = ({
  style = {},
  tag = 'div',
  children,
  fullWidth = false,
  contentWidth = 'full',
  anchorId,
  cssId,
  cssClass,
}) => {
  const { connectors: { connect, drag } } = useNode();

  const safeTag = sanitizeContainerTag(tag);
  const needsBoxedWrapper = contentWidth === 'boxed';
  const flexStyles = flexAlignFromTextAlign(style.textAlign);
  const hasVerticalAlign = !!style.justifyContent;

  const outerStyle: CSSProperties = {
    minHeight: '40px',
    ...style,
    ...(hasVerticalAlign ? { display: 'flex', flexDirection: 'column' } : {}),
    ...(fullWidth ? { width: '100vw', marginLeft: 'calc(-50vw + 50%)' } : {}),
    ...(needsBoxedWrapper ? {} : flexStyles),
  };

  // cssId is the user-facing "CSS ID" advanced field; it takes precedence
  // over anchorId (the scroll-jump anchor) when both happen to be set, since
  // only one `id` attribute can be emitted on the element.
  const idValue = cssId || anchorId || undefined;

  const el = React.createElement(
    safeTag,
    {
      ref: (ref: HTMLElement | null): void => { if (ref) connect(drag(ref)); },
      style: outerStyle,
      'data-craft-container': 'true',
      id: idValue,
      className: cssClass || undefined,
    },
    needsBoxedWrapper
      ? React.createElement('div', { style: { maxWidth: '1200px', margin: '0 auto', ...flexStyles } }, children)
      : children,
  );

  return el;
};

/* ---------- Craft config ---------- */

Container.craft = {
  displayName: 'Container',
  props: {
    style: {
      padding: '20px',
      minHeight: '100px',
      justifyContent: '',
      marginTop: '', marginRight: '', marginBottom: '', marginLeft: '',
      paddingTop: '', paddingRight: '', paddingBottom: '', paddingLeft: '',
      border: 'none',
      boxShadow: 'none',
      opacity: '1',
    },
    tag: 'div',
    fullWidth: false,
    contentWidth: 'full',
    anchorId: '',
    cssId: '',
    cssClass: '',
    animation: '',
    animationDelay: '0',
    hideOnDesktop: false,
    hideOnTablet: false,
    hideOnMobile: false,
  },
  rules: {
    canDrag: () => true,
    canMoveIn: () => true,
    canMoveOut: () => true,
  },
};

/* ---------- HTML export ---------- */

(Container as any).toHtml = (props: ContainerProps, childrenHtml: string) => {
  const tag = sanitizeContainerTag(props.tag);
  const isBoxed = props.contentWidth === 'boxed';
  const flexStyles = flexAlignFromTextAlign(props.style?.textAlign);
  const hasVerticalAlign = !!props.style?.justifyContent;

  const outerCss: CSSProperties = {
    ...props.style,
    ...(hasVerticalAlign ? { display: 'flex', flexDirection: 'column' } : {}),
    ...(isBoxed ? {} : flexStyles),
  };

  if (props.fullWidth) {
    outerCss.width = '100vw';
    outerCss.marginLeft = 'calc(-50vw + 50%)';
  }

  const styleStr = cssPropsToString(outerCss);
  // cssId wins over anchorId when both are set (see the render fn above for why).
  const idValue = props.cssId || props.anchorId;
  const idAttr = idValue ? ` id="${escapeAttr(idValue)}"` : '';
  const classAttr = props.cssClass ? ` class="${escapeAttr(props.cssClass)}"` : '';

  if (isBoxed) {
    const innerStyle = cssPropsToString({ maxWidth: '1200px', margin: '0 auto', ...flexStyles });
    return { html: `<${tag}${idAttr}${classAttr}${styleStr ? ` style="${styleStr}"` : ''}><div${innerStyle ? ` style="${innerStyle}"` : ''}>${childrenHtml}</div></${tag}>` };
  }

  return { html: `<${tag}${idAttr}${classAttr}${styleStr ? ` style="${styleStr}"` : ''}>${childrenHtml}</${tag}>` };
};
