import { useState, useEffect, useMemo, useRef, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { Meta, StoryObj, Decorator } from '@storybook/react-webpack5';
import { fn } from 'storybook/test';
import { Bank, Briefcase, Star, Travel } from '@transferwise/icons';
import {
  createSandboxStory,
  globalScope,
} from '../../../.storybook/components/sandbox/SandboxEditor';
import Button from '../../button';
import { CriticalBanner, type CriticalBannerProps } from './CriticalBanner';

/**
 * **Design guidance**: <a href="https://docs.wise.design/components/critical-banner" target="_blank">wise.design/components/critical-banner</a>
 *
 * Can be controlled or uncontrolled. When `onToggle` is provided, the consumer manages the expanded state via `expanded` and the banner calls `onToggle` when the user clicks the chevron. When `onToggle` is not provided, the banner manages its own expanded state internally and toggles it when the user clicks the chevron.
 */
const meta = {
  component: CriticalBanner,
  title: 'Prompts/CriticalBanner',
  tags: ['new'],
  parameters: { docs: { toc: true }, padding: '0' },
  args: {
    title: 'Your account requires verification',
    description: 'Please verify your identity to continue using all features.',
    sentiment: 'negative',
    expanded: true,
    action: { label: 'Verify now', onClick: fn() },
    actionSecondary: undefined,
    media: undefined,
    id: undefined,
    className: undefined,
    'data-testid': undefined,
    onToggle: fn(),
  },
  argTypes: {
    sentiment: {
      control: 'radio',
      options: ['negative', 'warning', 'neutral', 'success'],
    },
    title: {
      control: 'text',
      table: { type: { summary: 'ReactNode' } },
    },
    description: {
      control: 'text',
      table: { type: { summary: 'ReactNode' } },
    },
    expanded: { control: 'boolean' },
    media: {
      control: false,
      description: 'Replaces the default Status Icon with a custom image or AvatarView.',
      table: {
        type: {
          summary: 'PromptMedia',
          detail:
            '{ imgSrc?: string; avatar?: { imgSrc?: string; profileName?: string; profileType?: string; asset?: ReactNode; badge?: { flagCode: string } }; aria-label?: string }',
        },
      },
    },
    action: {
      control: false,
      table: {
        type: {
          summary: 'CriticalBannerAction',
          detail: '{ label: ReactNode; onClick?: () => void; href?: string; target?: string }',
        },
      },
    },
    actionSecondary: {
      control: false,
      table: {
        type: {
          summary: 'CriticalBannerActionSecondary',
          detail: '{ label: ReactNode; onClick?: () => void; href?: string; target?: string }',
        },
      },
    },
    id: { table: { category: 'Common' } },
    className: { table: { category: 'Common' } },
    'data-testid': { table: { category: 'Common' } },
    onToggle: { table: { disable: true } },
  },
} satisfies Meta<typeof CriticalBanner>;

export default meta;
type Story = StoryObj<typeof CriticalBanner>;

type PreviewStoryArgs = CriticalBannerProps & {
  previewMedia: CriticalBannerProps['media'];
  previewSecondaryAction: boolean;
};

const MEDIA_OPTIONS: Record<string, CriticalBannerProps['media']> = {
  undefined,
  'Custom image': { imgSrc: '../../wise-card.svg' },
  'Avatar: Photo': { avatar: { imgSrc: '../../avatar-rectangle-fox.webp' } },
  'Avatar: Business (GB)': {
    avatar: { profileType: 'BUSINESS', badge: { flagCode: 'GB' } },
  },
  'Avatar: Personal (EU)': {
    avatar: { profileType: 'PERSONAL', badge: { flagCode: 'EU' } },
  },
  'Avatar: Bank icon': { avatar: { asset: <Bank /> } },
  'Avatar: Star icon': { avatar: { asset: <Star /> } },
  'Avatar: Travel icon': { avatar: { asset: <Travel /> } },
  'Avatar: Briefcase icon': { avatar: { asset: <Briefcase /> } },
  'Avatar: Initials': { avatar: { profileName: 'John Doe' } },
};

const previewArgTypes = {
  previewMedia: {
    name: 'Preview with `media`',
    control: 'select',
    options: Object.keys(MEDIA_OPTIONS),
    mapping: MEDIA_OPTIONS,
    table: { category: 'Storybook Preview' },
  },
  previewSecondaryAction: {
    name: 'Preview with `actionSecondary`',
    control: 'boolean',
    table: { category: 'Storybook Preview' },
  },
} as const;

const hiddenArgTypes = {
  sentiment: { table: { disable: true } },
  title: { table: { disable: true } },
  description: { table: { disable: true } },
  action: { table: { disable: true } },
  actionSecondary: { table: { disable: true } },
  media: { table: { disable: true } },
  expanded: { table: { disable: true } },
  onToggle: { table: { disable: true } },
  id: { table: { disable: true } },
  className: { table: { disable: true } },
  'data-testid': { table: { disable: true } },
} as const;

const AnimatedEntryPreview = (args: CriticalBannerProps) => {
  const { id = 'animated-entry' } = args;
  const [bannerInstance, setBannerInstance] = useState(0);
  const animatedBanner = useMemo(
    () => ({
      ...args,
      id: `${id}-${bannerInstance}`,
    }),
    [args, bannerInstance, id],
  );

  return (
    <>
      <CriticalBanner key={animatedBanner.id} {...animatedBanner} />
      <div className="p-t-3">
        <Button block v2 onClick={() => setBannerInstance((current) => current + 1)}>
          Replay
        </Button>
      </div>
    </>
  );
};

/** Renders children inside an iframe that inherits the parent page's stylesheets. */
function IframeContainer({ width, children }: { width: number; children: ReactNode }) {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [mountNode, setMountNode] = useState<HTMLElement | null>(null);

  useEffect(() => {
    const iframe = iframeRef.current;
    if (!iframe) return;

    const doc = iframe.contentDocument;
    if (!doc) return;

    doc.head.replaceChildren(
      ...Array.from(document.head.querySelectorAll('link[rel="stylesheet"], style')).map((node) =>
        node.cloneNode(true),
      ),
    );

    doc.body.style.margin = '0';
    setMountNode(doc.body);
  }, []);

  return (
    <iframe
      ref={iframeRef}
      title={`${width}px preview`}
      sandbox="allow-same-origin"
      style={{
        width,
        height: 300,
        border: '1px solid var(--color-border-neutral)',
        borderRadius: 8,
        overflow: 'hidden',
      }}
    >
      {mountNode && createPortal(children, mountNode)}
    </iframe>
  );
}

const ContainerDecorator =
  (gap = '1rem'): Decorator =>
  (Story) => (
    <div
      style={{
        width: '100%',
        display: 'flex',
        flexDirection: 'column',
        gap,
      }}
    >
      <Story />
    </div>
  );

/**
 * Interactive playground with all controls.
 */
export const Playground: StoryObj<PreviewStoryArgs> = {
  args: {
    previewMedia: undefined,
    previewSecondaryAction: true,
  },
  argTypes: {
    ...previewArgTypes,
  },
  render: function Render(args: PreviewStoryArgs) {
    const { previewMedia, previewSecondaryAction, ...props } = args;
    const [expanded, setExpanded] = useState(args.expanded);

    useEffect(() => {
      setExpanded(args.expanded);
    }, [args.expanded]);

    return (
      <CriticalBanner
        {...props}
        expanded={expanded}
        media={previewMedia}
        actionSecondary={
          previewSecondaryAction ? { label: 'Learn more', onClick: fn() } : undefined
        }
        onToggle={() => setExpanded(!expanded)}
      />
    );
  },
};

export const Sandbox = createSandboxStory({
  code: `const App = () => {
  const [expanded, setExpanded] = React.useState(true);

  return (
    <CriticalBanner
      sentiment="negative"
      title="Your account requires verification"
      description="Please verify your identity to continue using all features."
      action={{ label: 'Verify now', onClick: () => console.log('verify') }}
      actionSecondary={{ label: 'Learn more', onClick: () => console.log('learn more') }}
      media={{ avatar: { asset: <Bank /> } }}
      expanded={expanded}
      onToggle={() => setExpanded((prev) => !prev)}
    />
  );
};`,
  scope: globalScope,
});

/**
 * There are four sentiments, with negative being the default.
 */
export const Sentiments: Story = {
  decorators: [ContainerDecorator('2rem')],
  parameters: {
    docs: {
      source: {
        code: `<CriticalBanner sentiment="negative" ... />
<CriticalBanner sentiment="warning" ... />
<CriticalBanner sentiment="neutral" ... />
<CriticalBanner sentiment="success" ... />`,
      },
    },
  },
  argTypes: hiddenArgTypes,
  render: function Render() {
    return (
      <>
        {(['negative', 'warning', 'neutral', 'success'] as const).map((sentiment) => (
          <CriticalBanner
            key={sentiment}
            sentiment={sentiment}
            title={`This is a critical banner with ${sentiment} sentiment`}
            description="Complete the required action to regain full access."
            action={{ label: 'Take action', onClick: fn() }}
            actionSecondary={{ label: 'Learn more', onClick: fn() }}
          />
        ))}
      </>
    );
  },
};

/**
 * Banners can have just a primary action, both primary and secondary, or no actions at all.
 */
export const Actions: Story = {
  decorators: [ContainerDecorator('2rem')],
  parameters: {
    docs: {
      source: {
        code: `<CriticalBanner 
  title="..." 
/>
<CriticalBanner 
  title="..."
  action={{ label: 'Verify identity', onClick: () => {} }} />
<CriticalBanner
  title="..."
  action={{ label: 'Verify now', onClick: () => {} }}
  actionSecondary={{ label: 'Contact support', onClick: () => {} }}
/>`,
      },
    },
  },
  argTypes: hiddenArgTypes,
  render: function Render() {
    return (
      <>
        <CriticalBanner sentiment="negative" title="No actions" description="Description" />
        <CriticalBanner
          sentiment="warning"
          title="Primary action only"
          description="Description"
          action={{ label: 'Verify identity', onClick: fn() }}
        />
        <CriticalBanner
          sentiment="success"
          title="Primary and secondary actions"
          description="Description"
          action={{ label: 'Verify now', onClick: fn() }}
          actionSecondary={{ label: 'Contact support', onClick: fn() }}
        />
      </>
    );
  },
};

/**
 * Each sentiment has a default status icon. Override it with a custom image or an AvatarView.
 */
export const MediaTypes: Story = {
  decorators: [ContainerDecorator('2rem')],
  parameters: {
    docs: {
      source: {
        code: `{/* Default — sentiment icon */}
<CriticalBanner sentiment="neutral" />

{/* Custom image */}
<CriticalBanner media={{ imgSrc: '/card.svg', 'aria-label': 'Wise card' }} ... />

{/* Avatar with photo */}
<CriticalBanner media={{ avatar: { imgSrc: '/photo.webp' } }} ... />

{/* Avatar with initials */}
<CriticalBanner media={{ avatar: { profileName: 'John Doe' } }} ... />

{/* Avatar with custom icon */}
<CriticalBanner media={{ avatar: { asset: <Bank /> } }} ... />

{/* Business Profile with flag badge */}
<CriticalBanner media={{ avatar: { profileType: 'BUSINESS', badge: { flagCode: 'GB' } } }} ... />`,
      },
    },
  },
  argTypes: hiddenArgTypes,
  render: function Render() {
    const [expandedStates, setExpandedStates] = useState<Record<string, boolean>>({
      default: true,
      image: true,
      avatarPhoto: true,
      avatarInitials: true,
      avatarIcon: true,
      businessBadge: true,
      personalBadge: true,
    });

    const toggle = (key: string) => setExpandedStates((prev) => ({ ...prev, [key]: !prev[key] }));

    return (
      <>
        <CriticalBanner
          sentiment="neutral"
          title="Default status icon"
          description="When no media is provided, the sentiment's default icon is displayed."
          action={{ label: 'Continue', onClick: fn() }}
          expanded={expandedStates.default}
          onToggle={() => toggle('default')}
        />
        <CriticalBanner
          sentiment="neutral"
          title="Custom image"
          description="Your card has been blocked for security reasons."
          media={{ imgSrc: '../../wise-card.svg', 'aria-label': 'Wise debit card' }}
          action={{ label: 'Unblock card', onClick: fn() }}
          expanded={expandedStates.image}
          onToggle={() => toggle('image')}
        />
        <CriticalBanner
          sentiment="neutral"
          title="Avatar with photo"
          description="The default badge is the status icon for the sentiment."
          media={{ avatar: { imgSrc: '../../avatar-rectangle-fox.webp' } }}
          action={{ label: 'Update photo', onClick: fn() }}
          expanded={expandedStates.avatarPhoto}
          onToggle={() => toggle('avatarPhoto')}
        />
        <CriticalBanner
          sentiment="neutral"
          title="Avatar with initials"
          description="The default badge is the status icon for the sentiment."
          media={{ avatar: { profileName: 'John Doe' } }}
          action={{ label: 'Complete profile', onClick: fn() }}
          expanded={expandedStates.avatarInitials}
          onToggle={() => toggle('avatarInitials')}
        />
        <CriticalBanner
          sentiment="neutral"
          title="Avatar with custom icon"
          description="The default badge is the status icon for the sentiment."
          media={{ avatar: { asset: <Bank title="Bank account" /> } }}
          action={{ label: 'Connect bank', onClick: fn() }}
          expanded={expandedStates.avatarIcon}
          onToggle={() => toggle('avatarIcon')}
        />
        <CriticalBanner
          sentiment="neutral"
          title="Business profile with badge"
          description="Your business account requires additional documentation."
          media={{ avatar: { profileType: 'BUSINESS', badge: { flagCode: 'GB' } } }}
          action={{ label: 'Upload documents', onClick: fn() }}
          expanded={expandedStates.businessBadge}
          onToggle={() => toggle('businessBadge')}
        />
      </>
    );
  },
};

/**
 * The banner can be fully controlled — the parent can pass in the `expanded` state.
 * Clicking the chevron calls `onToggle`, and external buttons can also toggle expansion.
 * We recommend tracking the state that the user has set `expanded` to and keeping it there on page refresh.
 *
 * When the banner is shown on wider screens, it will always be fully expanded.
 */
export const Expanded: Story = {
  argTypes: hiddenArgTypes,
  render: function Render() {
    const [expanded, setExpanded] = useState(true);

    return (
      <>
        <div className="p-b-3">
          <Button v2 onClick={() => setExpanded(!expanded)}>
            {expanded ? 'Collapse externally' : 'Expand externally'}
          </Button>
        </div>
        <IframeContainer width={350}>
          <CriticalBanner
            sentiment="negative"
            title="Your account requires verification"
            description="Please verify your identity to continue using all features. This process typically takes 2–3 minutes."
            action={{ label: 'Verify now', onClick: fn() }}
            actionSecondary={{ label: 'Learn more', onClick: fn() }}
            expanded={expanded}
            onToggle={() => setExpanded(!expanded)}
          />
        </IframeContainer>
      </>
    );
  },
  parameters: {
    docs: {
      source: {
        code: `const [expanded, setExpanded] = useState(true);

<Button onClick={() => setExpanded(!expanded)}>
  {expanded ? 'Collapse' : 'Expand'}
</Button>

<CriticalBanner
  ...
  expanded={expanded}
  onToggle={() => setExpanded(!expanded)}
/>`,
      },
    },
  },
};

/**
 * When the banner has no title, the description remains visible when collapsed and
 * is clamped to 2 lines. With a title, only the title is shown when collapsed.
 */
export const CollapsedBehaviour: Story = {
  decorators: [ContainerDecorator()],

  argTypes: hiddenArgTypes,
  render: function Render() {
    const [expanded1, setExpanded1] = useState(false);
    const [expanded2, setExpanded2] = useState(false);

    return (
      <IframeContainer width={650}>
        <CriticalBanner
          sentiment="negative"
          title="Collapsed with title"
          description="This description is hidden when collapsed because the title provides enough context."
          action={{ label: 'Take action', onClick: fn() }}
          expanded={expanded1}
          onToggle={() => setExpanded1(!expanded1)}
        />
        <CriticalBanner
          className="m-t-3"
          sentiment="warning"
          description="When there is no title, the description stays visible but is clamped to a maximum of two lines. This ensures the user always sees some context even in the collapsed state, because there is no title to summarise the message."
          action={{ label: 'Take action', onClick: fn() }}
          expanded={expanded2}
          onToggle={() => setExpanded2(!expanded2)}
        />
      </IframeContainer>
    );
  },
  parameters: {
    docs: {
      canvas: { sourceState: 'hidden' },
    },
  },
};

/**
 * Above 768px the collapse toggle disappears and the banner is always fully expanded. The buttons are not block displayed.
 * Below 768px the user can collapse and expand the banner and the buttons are displayed as block elements.
 * Eventually the buttons stack.
 */
export const Responsiveness: Story = {
  parameters: {
    docs: {
      canvas: { sourceState: 'hidden' },
    },
  },
  argTypes: hiddenArgTypes,
  render: function Render() {
    return (
      <div style={{ display: 'flex', gap: 16, alignItems: 'start', minWidth: 1700 }}>
        {[200, 350, 800].map((width) => (
          <IframeContainer key={width} width={width}>
            <CriticalBanner
              sentiment="warning"
              title={`At ${width}px the buttons are ${width === 200 ? 'stacked' : width === 350 ? 'full-width' : 'left-aligned'}`}
              description="Please verify your identity to continue using all features."
              action={{ label: 'Verify now', onClick: fn() }}
              actionSecondary={{ label: 'Learn more', onClick: fn() }}
            />
          </IframeContainer>
        ))}
      </div>
    );
  },
};

/**
 * Mobile viewport regression test for the overhang.
 * The overhang is always applied below 600px.
 */
export const MobileOverhang: Story = {
  argTypes: hiddenArgTypes,
  render: function Render() {
    return (
      <div style={{ width: 350 }}>
        <CriticalBanner
          sentiment="negative"
          title="Your account requires verification"
          description="Please verify your identity to continue using all features."
          action={{ label: 'Verify now', onClick: fn() }}
        />
      </div>
    );
  },
  parameters: {
    docs: {
      canvas: { sourceState: 'hidden' },
    },
  },
};

/**
 * Mobile-only entry animation: the banner reveals from above.
 */
export const AnimatedEntry: Story = {
  args: {
    title: 'Your account is restricted.',
    description: 'Review your personal details to get your account active again.',
    action: { label: 'Review details', onClick: fn() },
  },
  argTypes: hiddenArgTypes,
  render: (args) => <AnimatedEntryPreview {...args} />,
  parameters: {
    layout: 'fullscreen',
    docs: {
      canvas: { sourceState: 'hidden' },
    },
  },
};

/**
 * The text container is capped at 480px for optimal readability, even though
 * the banner itself stretches to the full available width.
 */
export const ParagraphWidth: Story = {
  parameters: {
    docs: {
      canvas: { sourceState: 'hidden' },
    },
  },
  argTypes: hiddenArgTypes,
  render: function Render() {
    const [expanded, setExpanded] = useState(true);

    return (
      <IframeContainer width={1000}>
        <CriticalBanner
          sentiment="negative"
          title="The text container is capped at 480px for optimal readability, even though the banner itself stretches to the full available width."
          description="To restore access, you'll need to verify your identity. This involves confirming your personal details and uploading a valid government-issued ID. The process typically takes 2–3 minutes, and your account will be restored immediately after successful verification."
          action={{ label: 'Verify now', onClick: fn() }}
          expanded={expanded}
          onToggle={() => setExpanded(!expanded)}
        />
      </IframeContainer>
    );
  },
};
