import React, { forwardRef, useMemo, type ReactNode } from 'react';
import { Text, View, type TextProps, type ViewProps } from 'react-native';
import { Slot, dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
import type { ForgeIcon } from '@cdx-ui/icons';
import { CheckCircle, EmergencyHome, Info, Warning } from '@cdx-ui/icons';
import { cn, ParentContext, useParentContext, useStyleContext } from '@cdx-ui/utils';
import { Icon, type IconProps } from '../Icon';
import {
  alertActionsVariants,
  alertBodyVariants,
  alertDescriptionVariants,
  alertIconVariants,
  alertRootVariants,
  alertTextVariants,
  alertTitleVariants,
  type AlertSeverity,
  type AlertVariantProps,
} from './styles';

// =============================================================================
// CONTEXT
// =============================================================================

const SCOPE = 'ALERT';

interface AlertContextValue {
  severity: AlertSeverity;
}

const useAlertStyleContext = (): AlertContextValue => {
  const ctx = useStyleContext(SCOPE) as { severity?: AlertSeverity };
  return { severity: ctx?.severity ?? 'info' };
};

// Severity → default icon mapping
const SEVERITY_ICONS: Record<AlertSeverity, ForgeIcon> = {
  danger: EmergencyHome,
  warning: Warning,
  success: CheckCircle,
  info: Info,
};

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

export interface AlertProps extends ViewProps, AlertVariantProps {
  /**
   * Replace the root element with the consumer's child element via `Slot`.
   * When `true`, children are rendered as-is (no automatic icon/text/actions
   * layout splitting). Severity context is still provided so sub-components
   * receive the correct styles.
   */
  asChild?: boolean;
  /** Severity level that controls background colour, icon, and live-region urgency. */
  severity?: AlertSeverity;
  className?: string;
  /**
   * Sub-components (`Alert.Icon`, `Alert.Title`, `Alert.Description`,
   * `Alert.Actions`) must be rendered as **direct** children — wrapping them
   * in HOCs or `memo()` will break the automatic layout splitting.
   */
  children?: ReactNode;
}

const AlertRoot = forwardRef<View, AlertProps>(
  ({ asChild, severity = 'info', className, style, children, ...props }, ref) => {
    const isAssertive = severity === 'danger' || severity === 'warning';
    const parent = useParentContext();
    const ctx = useMemo(() => ({ ...parent, [SCOPE]: { severity } }), [parent, severity]);

    const rootClassName = cn(alertRootVariants({ severity }), className);
    const role = isAssertive ? 'alert' : 'status';
    const liveRegion = isAssertive ? 'assertive' : 'polite';
    const dataProps = mergeDataAttributes(props, dataAttributes({ slot: 'alert' }));

    if (asChild) {
      return (
        <ParentContext.Provider value={ctx}>
          <Slot
            ref={ref}
            className={rootClassName}
            style={style}
            role={role}
            accessibilityLiveRegion={liveRegion}
            {...props}
            {...dataProps}
          >
            {children}
          </Slot>
        </ParentContext.Provider>
      );
    }

    // Split children into three regions:
    //   1. Alert.Icon    — rendered at inline-start (left)
    //   2. Alert.Actions — rendered at inline-end (right), beside the text
    //   3. Everything else (Title, Description) — text column, flex-1
    //
    // Layout: [Icon]  [flex-1 flex-row: [text flex-1]  [Actions]]
    const childArray = React.Children.toArray(children);
    const iconChild = childArray.find(
      (child) =>
        React.isValidElement(child) && (child as React.ReactElement).type === AlertIconComponent,
    );
    const actionsChild = childArray.find(
      (child) => React.isValidElement(child) && (child as React.ReactElement).type === AlertActions,
    );
    const textChildren = childArray.filter(
      (child) =>
        !React.isValidElement(child) ||
        ((child as React.ReactElement).type !== AlertIconComponent &&
          (child as React.ReactElement).type !== AlertActions),
    );
    const hasBody = textChildren.length > 0 || !!actionsChild;

    return (
      <ParentContext.Provider value={ctx}>
        <View
          ref={ref}
          className={rootClassName}
          style={style}
          role={role}
          accessibilityLiveRegion={liveRegion}
          {...props}
          {...dataProps}
        >
          {iconChild}
          {hasBody ? (
            <View className={alertBodyVariants()}>
              {textChildren.length > 0 ? (
                <View className={alertTextVariants()}>{textChildren}</View>
              ) : null}
              {actionsChild}
            </View>
          ) : null}
        </View>
      </ParentContext.Provider>
    );
  },
);

AlertRoot.displayName = 'Alert';

// =============================================================================
// ICON
// =============================================================================

export interface AlertIconProps extends Omit<IconProps, 'as'> {
  asChild?: boolean;
  /**
   * Override the auto-rendered severity icon. Omit to use the default icon for
   * the current severity (EmergencyHome, Warning, CheckCircle, or Info).
   * Not required when `asChild` is set.
   */
  as?: ForgeIcon;
}

const AlertIconComponent = forwardRef<View, AlertIconProps>(
  ({ asChild, as: asOverride, className, style, ...props }, ref) => {
    const { severity } = useAlertStyleContext();
    const computedClassName = cn(alertIconVariants({ severity }), className);

    if (asChild) {
      return (
        <Slot
          ref={ref}
          className={computedClassName}
          style={style}
          {...props}
          {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-icon' }))}
        />
      );
    }

    return (
      <Icon
        as={asOverride ?? SEVERITY_ICONS[severity]}
        className={computedClassName}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-icon' }))}
      />
    );
  },
);

AlertIconComponent.displayName = 'Alert.Icon';

// =============================================================================
// TITLE
// =============================================================================

export interface AlertTitleProps extends TextProps {
  asChild?: boolean;
  className?: string;
  children?: ReactNode;
}

const AlertTitle = forwardRef<Text, AlertTitleProps>(
  ({ asChild, className, style, children, ...props }, ref) => {
    const { severity } = useAlertStyleContext();
    const computedClassName = cn(alertTitleVariants({ severity }), className);
    const Comp = asChild ? Slot : Text;

    return (
      <Comp
        ref={ref}
        className={computedClassName}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-title' }))}
      >
        {children}
      </Comp>
    );
  },
);

AlertTitle.displayName = 'Alert.Title';

// =============================================================================
// DESCRIPTION
// =============================================================================

export interface AlertDescriptionProps extends TextProps {
  asChild?: boolean;
  className?: string;
  children?: ReactNode;
}

const AlertDescription = forwardRef<Text, AlertDescriptionProps>(
  ({ asChild, className, style, children, ...props }, ref) => {
    const { severity } = useAlertStyleContext();
    const computedClassName = cn(alertDescriptionVariants({ severity }), className);
    const Comp = asChild ? Slot : Text;

    return (
      <Comp
        ref={ref}
        className={computedClassName}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-description' }))}
      >
        {children}
      </Comp>
    );
  },
);

AlertDescription.displayName = 'Alert.Description';

// =============================================================================
// ACTIONS
// =============================================================================

export interface AlertActionsProps extends ViewProps {
  /**
   * Replace the wrapper element with the consumer's child via `Slot`.
   * **Note:** when `true`, automatic `color={severity}` injection on
   * direct `Button`/`IconButton` children is bypassed — the consumer
   * must set `color` explicitly.
   */
  asChild?: boolean;
  className?: string;
  children?: ReactNode;
}

const AlertActions = forwardRef<View, AlertActionsProps>(
  ({ asChild, className, style, children, ...props }, ref) => {
    const { severity } = useAlertStyleContext();
    const computedClassName = cn(alertActionsVariants(), className);
    const Comp = asChild ? Slot : View;

    // Auto-inject color={severity} on direct Button / IconButton children unless
    // the consumer already provides a color prop.
    const injectedChildren = React.Children.map(children, (child) => {
      if (!React.isValidElement<{ color?: unknown }>(child)) return child;
      if ('color' in child.props) return child;
      return React.cloneElement(child, { color: severity });
    });

    return (
      <Comp
        ref={ref}
        className={computedClassName}
        style={style}
        {...props}
        {...mergeDataAttributes(props, dataAttributes({ slot: 'alert-actions' }))}
      >
        {asChild ? children : injectedChildren}
      </Comp>
    );
  },
);

AlertActions.displayName = 'Alert.Actions';

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

type AlertCompoundComponent = typeof AlertRoot & {
  Icon: typeof AlertIconComponent;
  Title: typeof AlertTitle;
  Description: typeof AlertDescription;
  Actions: typeof AlertActions;
};

export const Alert = Object.assign(AlertRoot, {
  Icon: AlertIconComponent,
  Title: AlertTitle,
  Description: AlertDescription,
  Actions: AlertActions,
}) as AlertCompoundComponent;

export type { AlertVariantProps, AlertSeverity };
