import { Meta, StoryObj } from '@storybook/react-webpack5';
import { Bank, Defrost } from '@transferwise/icons';
import Body from '../body';
import Button from '../button';
import Header from '../header';
import Link from '../link';
import IconButton from '../iconButton';
import StatusIcon from '../statusIcon';
import AvatarView from '../avatarView';
import SentimentSurface from './SentimentSurface';
import type { Sentiment, Emphasis } from './SentimentSurface.types';

const withComponentGrid = (Story: () => JSX.Element) => (
  <div
    style={{
      width: '100%',
      display: 'flex',
      flexDirection: 'column',
      gap: '1rem',
      maxWidth: '800px',
    }}
  >
    <Story />
  </div>
);

/**
 * SentimentSurface is a polymorphic container component that exposes and, optionally, applies
 * contextual colour tokens as CSS custom properties, based on sentiment types (`negative`,
 * `warning`, `neutral`, `success`, `proposition`).
 */
const meta: Meta<typeof SentimentSurface> = {
  component: SentimentSurface,
  title: 'Foundations/SentimentSurface',
  tags: ['early-access'],
  argTypes: {
    sentiment: {
      control: 'select',
      options: ['negative', 'warning', 'neutral', 'success', 'proposition'],
      description: 'The sentiment type that determines the colour scheme',
      table: {
        type: { summary: 'Sentiment' },
      },
    },
    emphasis: {
      control: 'inline-radio',
      options: ['base', 'elevated'],
      description: 'The emphasis level affecting background and text contrast',
      table: {
        defaultValue: { summary: '"base"' },
      },
    },
    hasBaseStyles: {
      control: 'boolean',
      description:
        'If true, sets the `background-color` and `color` on the container. Otherwise, only exposes the tokens as CSS custom properties without rendering.',
      table: {
        defaultValue: { summary: 'true' },
      },
    },
    as: {
      control: 'text',
      description: 'The underlying HTML element to render.',
      table: {
        defaultValue: {
          summary: '"div"',
        },
        type: { summary: 'string' },
      },
    },
    className: {
      table: {
        type: { summary: 'string' },
      },
      control: 'text',
    },
    id: {
      table: {
        type: { summary: 'string' },
      },
      control: 'text',
    },
    'data-testid': {
      control: 'text',
      table: {
        type: { summary: 'string' },
      },
    },
    children: {
      table: {
        type: { summary: 'ReactNode' },
      },
      control: false,
    },
  },
  args: {
    sentiment: 'neutral',
    emphasis: 'base',
    hasBaseStyles: undefined,
    as: undefined,
    className: undefined,
    id: undefined,
    'data-testid': undefined,
    children: 'This is a sentiment surface',
  },
};

export default meta;

type Story = StoryObj<typeof SentimentSurface>;

/**
 * The Playground story allows you to experiment with all the props of the SentimentSurface component.
 * Use the controls below to customise the sentiment type, emphasis level, and content.
 */
export const Playground: Story = {
  args: {
    sentiment: 'negative',
    emphasis: 'base',
    children: (
      <div className="p-a-2">
        This is a custom div demonstrating a sentiment surface. It exposes CSS custom properties for
        the selected sentiment type and emphasis level and, optionally, applies background and text
        colours.
      </div>
    ),
  },
};

const sentiments: Sentiment[] = ['negative', 'warning', 'neutral', 'success', 'proposition'];
const emphasisLevels: Emphasis[] = ['base', 'elevated'];
const tokenCategories = [
  {
    name: 'Content',
    tokens: [
      '--color-sentiment-content-primary',
      '--color-sentiment-content-primary-hover',
      '--color-sentiment-content-primary-active',
    ],
  },
  {
    name: 'Interactive Primary',
    tokens: [
      '--color-sentiment-interactive-primary',
      '--color-sentiment-interactive-primary-hover',
      '--color-sentiment-interactive-primary-active',
    ],
  },
  {
    name: 'Interactive Secondary',
    tokens: [
      '--color-sentiment-interactive-secondary',
      '--color-sentiment-interactive-secondary-hover',
      '--color-sentiment-interactive-secondary-active',
    ],
  },
  {
    name: 'Interactive Control',
    tokens: [
      '--color-sentiment-interactive-control',
      '--color-sentiment-interactive-control-hover',
      '--color-sentiment-interactive-control-active',
    ],
  },
  {
    name: 'Background Surface',
    tokens: [
      '--color-sentiment-background-surface',
      '--color-sentiment-background-surface-hover',
      '--color-sentiment-background-surface-active',
    ],
  },
];

const TokenSwatch = ({ token }: { token: string }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '4px' }}>
    <div
      style={{
        width: '32px',
        height: '32px',
        borderRadius: '4px',
        backgroundColor: `var(${token})`,
        border: '1px solid rgba(0,0,0,0.1)',
        flexShrink: 0,
      }}
    />
    <code style={{ fontSize: '12px', color: 'var(--color-sentiment-content-primary)' }}>
      {token}
    </code>
  </div>
);

/**
 * Each sentiment type has two emphasis levels – `base` and `elevated` – to provide different levels of visual prominence.
 *
 * **When to use:**
 *
 * - `Base`: For inline notifications, subtle callouts, or when sentiment needs to be communicated without drawing too much attention
 * - `Elevated`: For prominent banners, critical alerts, or when the sentiment should be immediately noticeable
 *
 */
export const EmphasisLevels: Story = {
  render: (args) => (
    <>
      <SentimentSurface {...args} sentiment="negative" emphasis="base">
        <div className="p-a-2">Negative - Base emphasis</div>
      </SentimentSurface>
      <SentimentSurface {...args} sentiment="negative" emphasis="elevated">
        <div className="p-a-2">Negative - Elevated emphasis</div>
      </SentimentSurface>
      <SentimentSurface {...args} sentiment="success" emphasis="base">
        <div className="p-a-2">Success - Base emphasis</div>
      </SentimentSurface>
      <SentimentSurface {...args} sentiment="success" emphasis="elevated">
        <div className="p-a-2">Success - Elevated emphasis</div>
      </SentimentSurface>
    </>
  ),
  parameters: {
    controls: { disable: true },
  },
  decorators: [withComponentGrid],
};

/**
 * By default, `SentimentSurface` exposes the tokens as CSS custom properties and also applies
 * the base styles for `background-color` and `color`. However, you can choose to expose said
 * properties without rendering them, by setting the `hasBaseStyles` prop to `false`.
 * This is useful when you want to create custom components that adapt to the sentiment context
 * without applying default styles.
 */
export const RenderingBaseStyles: Story = {
  render: (args) => (
    <>
      <SentimentSurface
        sentiment="success"
        style={{ border: `1px dashed var(--color-sentiment-content-primary)` }}
      >
        <div className="p-a-2">This example exposes and applies the tokens.</div>
      </SentimentSurface>
      <SentimentSurface
        sentiment="success"
        hasBaseStyles={false}
        style={{ border: `1px dashed var(--color-sentiment-content-primary)` }}
      >
        <div className="p-a-2">This example only exposes the tokens without applying them.</div>
        <div
          className="p-a-2"
          style={{
            backgroundColor: 'var(--color-sentiment-background-surface)',
            color: 'var(--color-sentiment-content-primary)',
          }}
        >
          Which means you have to apply them yourself.
        </div>
      </SentimentSurface>
    </>
  ),
  parameters: {
    controls: { disable: true },
    docs: {
      source: {
        // Storybook strips out props that are set to false, so we need to hack it back in :/
        transform: async (source: string) => {
          if (source.includes('hasBaseStyles')) {
            return source;
          }

          return source.replace(
            /(<\/SentimentSurface>\s+<SentimentSurface(\s+)sentiment="success")/g,
            '$1$2hasBaseStyles={false}',
          );
        },
      },
    },
  },
  decorators: [withComponentGrid],
};

/**
 * Sentiment surfaces can be nested to create layered contexts. Each nested surface establishes its own colour context through CSS custom properties.
 */
export const NestingSurfaces: Story = {
  parameters: {
    controls: { disable: true },
  },
  /**
   * As agreed with the design team, this story is temporarily hidden from the public view until we
   * have proper discussion and documentation around when and how to use nested SentimentSurfaces.
   * This is to prevent misuse and confusion among consumers.
   */
  tags: ['!dev', '!autodocs'],
  render: (args) => {
    return (
      <SentimentSurface sentiment="success" emphasis="base" className="p-a-1">
        Success base sentiment surface
        <SentimentSurface sentiment="neutral" emphasis="elevated" className="p-a-1 m-t-1">
          Neutral elevated sentiment surface
          <SentimentSurface sentiment="warning" emphasis="base" className="p-a-1 m-t-1">
            Warning base sentiment surface
          </SentimentSurface>
        </SentimentSurface>
      </SentimentSurface>
    );
  },
};

/**
 * Some DS components (namely `Button`, `Link`, `StatusIcon`, `IconButton`) automatically adapt to the sentiment context when used within a `SentimentSurface`. <br/>
 *
 * > ⚠️ You should not mix sentiments within the same surface. For example, avoid using a `StatusIcon` with `sentiment="negative"` inside a `SentimentSurface` with `sentiment="success"`.
 */
export const SentimentAwareComponents: Story = {
  name: 'Sentiment-aware Components',
  parameters: {
    controls: { disable: true },
    docs: {
      canvas: {
        sourceState: 'hidden',
      },
    },
  },
  render: function Render(args) {
    const BODY = [
      <Body key="body">
        This text and its <Link href="#">inline links</Link> should use sentiment-matched colour.
      </Body>,
      <Button key="button1" v2 priority="primary" size="sm">
        Primary
      </Button>,
      <Button key="button2" v2 priority="secondary" size="sm">
        Secondary
      </Button>,
      <IconButton key="iconButton" size={32}>
        <Defrost />
      </IconButton>,
      <AvatarView key="avatar" size={32}>
        <Bank />
      </AvatarView>,
      <AvatarView key="avatar-initials" size={32} profileName="John Doe" />,
    ];
    const STATUS_SUCCESS = <StatusIcon size={32} sentiment="success" />;
    const STATUS_NEGATIVE = <StatusIcon size={32} sentiment="negative" />;
    const AVATAR_SUCCESS = (
      <AvatarView
        size={32}
        imgSrc="../avatar-rectangle-fox.webp"
        badge={{ status: 'success' }}
        interactive
      />
    );
    const AVATAR_NEGATIVE = (
      <AvatarView
        size={32}
        imgSrc="../avatar-rectangle-fox.webp"
        badge={{ status: 'negative' }}
        interactive
      />
    );

    return (
      <>
        <Header level="group" title="Plain container" />
        <div className="p-a-2 m-b-2 sentimentAwareComponent">
          {BODY}
          {AVATAR_SUCCESS}
          {AVATAR_NEGATIVE}
          {STATUS_SUCCESS}
          {STATUS_NEGATIVE}
        </div>

        <Header level="group" title="Success sentiment surface" />
        <SentimentSurface sentiment="success">
          <div className="p-a-2 m-b-2 sentimentAwareComponent">
            {BODY}
            {AVATAR_SUCCESS}
            {STATUS_SUCCESS}
          </div>
        </SentimentSurface>

        <Header level="group" title="Negative sentiment surface" />
        <SentimentSurface sentiment="negative">
          <div className="p-a-2 m-b-2 sentimentAwareComponent">
            {BODY}
            {AVATAR_NEGATIVE}
            {STATUS_NEGATIVE}
          </div>
        </SentimentSurface>
      </>
    );
  },
  decorators: [
    withComponentGrid,
    (Story) => (
      <>
        <style>
          {`
            .sentimentAwareComponent {
              display: flex;
              flex-wrap: wrap;
              gap: var(--size-8);
              
              >:first-child {
                width: 100%;
              }
            }
          `}
        </style>
        <Story />
      </>
    ),
  ],
};

function CustomCard({ children, className }: { className?: string; children: React.ReactNode }) {
  return (
    <div
      className={`p-a-1 d-flex align-items-center justify-content-between custom-card ${className}`}
    >
      {children}
      <Button v2 size="sm">
        Interact with me
      </Button>
    </div>
  );
}

export const CustomComponents: Story = {
  tags: ['!autodocs', '!dev'],
  render: (args) => (
    <>
      <style>{`
        .custom-card {
          background-color: var(
            --color-sentiment-background-surface,
            var(--color-background-surface)
          );
          
          color: var(
            --color-sentiment-content-primary,
            var(--color-content-primary)
          );
          
          gap: var(--size-16);
        }
      `}</style>

      <CustomCard className="m-b-2">This component works outside a SentimentSurface</CustomCard>

      <SentimentSurface sentiment="negative">
        <CustomCard>This component adapts to the context of the SentimentSurface</CustomCard>
      </SentimentSurface>
    </>
  ),
  parameters: {
    docs: {
      source: {
        code: `
// CSS
.custom-card {
  color: var(
    --color-sentiment-content-primary,
    var(--color-content-primary)
  );
  border: 1px solid var(
    --color-sentiment-interactive-secondary,
    var(--color-border-neutral)
  );
  gap: var(--size-16);
}

// JSX
function CustomCard({ children }) {
  return (
    <div className="p-a-1 d-flex align-items-center justify-content-between custom-card">
      {children}
      <Button v2 size="sm">
        Interact with me
      </Button>
    </div>
  );
}

function View() {
  return (
    <>
      <CustomCard>This card works outside a SentimentSurface</CustomCard>
      
      <SentimentSurface sentiment="negative">
        <CustomCard>This card adapts to the warning sentiment</CustomCard>
      </SentimentSurface>
    </>
  );
}
`,
      },
    },
  },
};

/**
 * The component exposes token groups for all 5 sentiment types (`negative`, `warning`, `neutral`, `proposition` and `success`) each of them with two emphasis levels (`base` and `elevated`).
 */
export const ExposedTokens: Story = {
  render: () => (
    <section style={{ display: 'flex', flexDirection: 'column', gap: '32px', maxWidth: '100%' }}>
      {sentiments.map((sentiment) =>
        emphasisLevels.map((emphasis) => (
          <SentimentSurface
            key={`${sentiment}-${emphasis}`}
            sentiment={sentiment}
            emphasis={emphasis}
            style={{
              padding: '24px',
              borderRadius: '16px',
            }}
          >
            <h4
              className="d-block m-b-2 text-capitalize np-text-title-body"
              style={{
                color: 'inherit',
              }}
            >
              {sentiment} - {emphasis}
            </h4>
            <div
              style={{
                display: 'grid',
                gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
                gap: '24px',
              }}
            >
              {tokenCategories.map((category) => (
                <div key={category.name}>
                  <h5
                    style={{
                      color: 'inherit',
                      fontSize: '12px',
                      fontWeight: 600,
                      marginBottom: '8px',
                      textTransform: 'uppercase',
                      letterSpacing: '0.5px',
                      opacity: 0.8,
                    }}
                  >
                    {category.name}
                  </h5>
                  {category.tokens.map((token) => (
                    <TokenSwatch key={token} token={token} />
                  ))}
                </div>
              ))}
            </div>
          </SentimentSurface>
        )),
      )}
    </section>
  ),
  parameters: {
    controls: { disable: true },
    docs: {
      canvas: {
        sourceState: 'hidden',
      },
    },
  },
  decorators: [
    (Story: () => JSX.Element) => (
      <div style={{ padding: '2rem', maxWidth: '1200px' }}>
        <Story />
      </div>
    ),
  ],
};
