import { forwardRef, useMemo, type ReactNode } from 'react';
import { Text, View, type TextProps, type ViewProps } from 'react-native';
import {
  ListItemRoot as ListItemRootPrimitive,
  Slot,
  dataAttributes,
  mergeDataAttributes,
  type IListItemProps,
  type ListItemCrossAlign,
} from '@cdx-ui/primitives';
import { cn, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
import {
  listItemContentVariants,
  listItemDescriptionVariants,
  listItemLeadingSlotVariants,
  listItemMetaVariants,
  listItemRootVariants,
  listItemSectionHeaderLabelVariants,
  listItemSectionHeaderVariants,
  listItemTitleVariants,
  listItemTrailingSlotVariants,
  type ListItemVariantProps,
} from './styles';

const SCOPE = 'LIST_ITEM';

const useListItemStyleContext = (): Pick<ListItemVariantProps, 'crossAlign'> => {
  const ctx = useStyleContext(SCOPE) as ListItemVariantProps | undefined;
  return { crossAlign: ctx?.crossAlign ?? 'center' };
};

// =============================================================================
// ROOT
// =============================================================================

export interface ListItemProps extends Omit<IListItemProps, 'crossAlign'>, ListItemVariantProps {
  className?: string;
}

const ListItemRoot = forwardRef<View, ListItemProps>(
  (
    {
      size = 'default',
      showSeparator = true,
      crossAlign: crossAlignProp = 'center',
      className,
      style,
      ...props
    },
    ref,
  ) => {
    const crossAlign: ListItemCrossAlign = crossAlignProp ?? 'center';
    const parent = useParentContext();
    const ctx = useMemo(() => ({ ...parent, [SCOPE]: { crossAlign } }), [parent, crossAlign]);

    const computedClassName = cn(
      listItemRootVariants({ size, showSeparator, crossAlign }),
      className,
    );

    return (
      <ParentContext.Provider value={ctx}>
        <ListItemRootPrimitive
          ref={ref}
          className={computedClassName}
          crossAlign={crossAlign}
          style={style}
          {...props}
          {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item' }))}
        />
      </ParentContext.Provider>
    );
  },
);

ListItemRoot.displayName = 'ListItem';

// =============================================================================
// SLOTS & TEXT
// =============================================================================

const leadingSlotStyle = { flexShrink: 0 as const };

export interface ListItemLeadingSlotProps extends ViewProps {
  asChild?: boolean;
  className?: string;
  /**
   * Decorative leading content defaults to hidden from the accessibility tree.
   * Set to `false` when the leading region contains meaningful controls.
   * @default true
   */
  'aria-hidden'?: boolean;
}

const ListItemLeadingSlot = forwardRef<View, ListItemLeadingSlotProps>(
  ({ asChild, className, style, 'aria-hidden': ariaHidden, ...props }, ref) => {
    const accessibilityElementsHidden = ariaHidden !== false;
    const Comp = asChild ? Slot : View;
    return (
      <Comp
        ref={ref}
        accessibilityElementsHidden={accessibilityElementsHidden}
        aria-hidden={ariaHidden ?? true}
        className={cn(listItemLeadingSlotVariants(), className)}
        style={[leadingSlotStyle, style]}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item-leading' }))}
      />
    );
  },
);

ListItemLeadingSlot.displayName = 'ListItem.LeadingSlot';

const contentStyle = {
  flex: 1,
  flexDirection: 'column' as const,
  minWidth: 0,
};

export interface ListItemContentProps extends ViewProps {
  asChild?: boolean;
  className?: string;
}

const ListItemContent = forwardRef<View, ListItemContentProps>(
  ({ asChild, className, style, ...props }, ref) => {
    const { crossAlign } = useListItemStyleContext();
    const Comp = asChild ? Slot : View;

    return (
      <Comp
        ref={ref}
        className={cn(listItemContentVariants({ crossAlign }), className)}
        style={[contentStyle, style]}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item-content' }))}
      />
    );
  },
);

ListItemContent.displayName = 'ListItem.Content';

export interface ListItemTitleProps extends TextProps {
  asChild?: boolean;
  className?: string;
}

const ListItemTitle = forwardRef<Text, ListItemTitleProps>(
  ({ asChild, className, numberOfLines = 1, style, ...props }, ref) => {
    const Comp = asChild ? Slot : Text;
    return (
      <Comp
        ref={ref}
        className={cn(listItemTitleVariants(), className)}
        numberOfLines={numberOfLines}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item-title' }))}
      />
    );
  },
);

ListItemTitle.displayName = 'ListItem.Title';

export interface ListItemDescriptionProps extends TextProps {
  asChild?: boolean;
  className?: string;
}

const ListItemDescription = forwardRef<Text, ListItemDescriptionProps>(
  ({ asChild, className, style, ...props }, ref) => {
    const Comp = asChild ? Slot : Text;
    return (
      <Comp
        ref={ref}
        className={cn(listItemDescriptionVariants(), className)}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item-description' }))}
      />
    );
  },
);

ListItemDescription.displayName = 'ListItem.Description';

export interface ListItemMetaProps extends TextProps {
  asChild?: boolean;
  className?: string;
}

const ListItemMeta = forwardRef<Text, ListItemMetaProps>(
  ({ asChild, className, numberOfLines = 1, style, ...props }, ref) => {
    const Comp = asChild ? Slot : Text;
    return (
      <Comp
        ref={ref}
        className={cn(listItemMetaVariants(), className)}
        numberOfLines={numberOfLines}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item-meta' }))}
      />
    );
  },
);

ListItemMeta.displayName = 'ListItem.Meta';

export interface ListItemTrailingSlotProps extends ViewProps {
  asChild?: boolean;
  className?: string;
}

const ListItemTrailingSlot = forwardRef<View, ListItemTrailingSlotProps>(
  ({ asChild, className, style, ...props }, ref) => {
    const { crossAlign } = useListItemStyleContext();
    const Comp = asChild ? Slot : View;

    return (
      <Comp
        ref={ref}
        className={cn(listItemTrailingSlotVariants({ crossAlign }), className)}
        style={[{ flexShrink: 0 }, style]}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'list-item-trailing' }))}
      />
    );
  },
);

ListItemTrailingSlot.displayName = 'ListItem.TrailingSlot';

const wrapSectionHeaderLabel = (children: ReactNode): ReactNode => {
  if (typeof children === 'string' || typeof children === 'number') {
    return <Text className={cn(listItemSectionHeaderLabelVariants())}>{children}</Text>;
  }
  return children;
};

// =============================================================================
// SECTION HEADER
// =============================================================================

// TODO: ListItem.SectionHeader doesn't follow composition patterns (composing text, trailing prop, etc).

const sectionHeaderRow = {
  flexDirection: 'row' as const,
  alignItems: 'center' as const,
  justifyContent: 'space-between' as const,
  gap: 8,
};

const sectionHeaderLabelCell = {
  flex: 1,
  minWidth: 0,
  /** Keeps the label column from stretching to a taller trailing sibling (e.g. Chip). */
  alignSelf: 'center' as const,
};

const sectionHeaderTrailingCell = {
  flexShrink: 0,
  /** Prevents the trailing slot from stretching vertically and “filling” the row. */
  alignSelf: 'center' as const,
};

export interface ListItemSectionHeaderProps extends ViewProps {
  asChild?: boolean;
  className?: string;
  /** Primary section label (e.g. date heading). */
  children: ReactNode;
  /** Optional right-aligned adornment (e.g. section Chip). */
  trailing?: ReactNode;
  /**
   * Whether to expose the top divider marker for styling.
   * @default true
   */
  showDivider?: boolean;
}

const ListItemSectionHeader = forwardRef<View, ListItemSectionHeaderProps>(
  (
    {
      asChild,
      children,
      className,
      showDivider = true,
      style,
      trailing,
      accessibilityRole = 'header',
      ...props
    },
    ref,
  ) => {
    const Comp = asChild ? Slot : View;
    return (
      <Comp
        ref={ref}
        accessibilityRole={accessibilityRole}
        className={cn(listItemSectionHeaderVariants({ showDivider }), className)}
        style={[sectionHeaderRow, style]}
        {...props}
        {...mergeDataAttributes(
          props,
          dataAttributes({ slot: 'list-section-header', divider: showDivider }),
        )}
      >
        <View style={sectionHeaderLabelCell}>{wrapSectionHeaderLabel(children)}</View>
        {trailing ? <View style={sectionHeaderTrailingCell}>{trailing}</View> : null}
      </Comp>
    );
  },
);

ListItemSectionHeader.displayName = 'ListItem.SectionHeader';

// =============================================================================
// COMPOUND EXPORT
// =============================================================================

type ListItemCompound = typeof ListItemRoot & {
  LeadingSlot: typeof ListItemLeadingSlot;
  Content: typeof ListItemContent;
  Title: typeof ListItemTitle;
  Description: typeof ListItemDescription;
  Meta: typeof ListItemMeta;
  TrailingSlot: typeof ListItemTrailingSlot;
  SectionHeader: typeof ListItemSectionHeader;
};

export const ListItem = Object.assign(ListItemRoot, {
  LeadingSlot: ListItemLeadingSlot,
  Content: ListItemContent,
  Title: ListItemTitle,
  Description: ListItemDescription,
  Meta: ListItemMeta,
  TrailingSlot: ListItemTrailingSlot,
  SectionHeader: ListItemSectionHeader,
}) as ListItemCompound;

export type { ListItemCrossAlign, ListItemVariantProps };
export {
  listItemContentVariants,
  listItemDescriptionVariants,
  listItemLeadingSlotVariants,
  listItemMetaVariants,
  listItemRootVariants,
  listItemSectionHeaderLabelVariants,
  listItemSectionHeaderVariants,
  listItemTitleVariants,
  listItemTrailingSlotVariants,
} from './styles';
