import React, { useState, useCallback, memo, useRef } from 'react';
import type { Meta, StoryObj } from '@storybook/react-webpack5';
import * as AllIcons from '@transferwise/icons';
import iconsPkg from '@transferwise/icons/package.json';
import { ThemeProvider } from '@wise/components-theming';

import Chips, { type ChipValue } from '../chips/Chips';
import { SearchInput } from '../inputs/SearchInput';
import SnackbarProvider from '../snackbar/SnackbarProvider';
import useSnackbar from '../snackbar/useSnackbar';

const ICONS_VERSION: string = iconsPkg.version;

type IconComponent = React.FunctionComponent<{
  size?: '16' | '24' | '32' | 16 | 24 | 32;
  className?: string;
  title?: string;
}>;

const OLD_ICON_NAMES_MAP: Record<string, string> = {
  Alert: 'Warning',
  Ach: 'BankTransfer',
  OfficeExpenses: 'Stationery',
  Chat: 'SpeechBubble',
  Holidays: 'Beach',
  Cs: 'Headset',
  Family: 'Heart',
  Settings: 'Cog',
  Lightning: 'LightningBolt',
  Atm: 'InsertCard',
  SalesAndRoyalties: 'CashRegister',
  Tax: 'PercentageCircle',
  Recipients: 'People',
  Marketing: 'Target',
  DriversLicense: 'Car',
  PinCode: 'Dial',
  Rent: 'Building',
  Unlock: 'PadlockUnlocked',
  Lock: 'Padlock',
  CardNumber: 'CardDetail',
  Invite: 'GiftBox',
  Dont: 'SadEmoji',
  CardTransferwise: 'CardWise',
  ChipPin: 'Chip',
  Profile: 'Person',
  Home: 'House',
  PendingCircle: 'Clock',
  Activity: 'List',
  ExchangeRate: 'UpwardGraph',
  ContractServices: 'Handshake',
  Travel: 'Suitcase',
  TwoStep: 'MobileLock',
  EmailAndPhone: 'EmailAndMobile',
  Insights: 'Bulb',
  Salary: 'PayIn',
  Picture: 'Image',
  ECommerce: 'ShoppingBag',
  Feedback: 'SpeechBubbleMessage',
  Refresh: 'Reload',
  CostOfGoodsSold: 'Boxes',
  Expenses: 'PieChart',
  Pending: 'SpeechBubblePending',
  Help: 'QuestionMark',
  HelpCircle: 'QuestionMarkCircle',
  CalendarSuccess: 'CalendarCheck',
  Comments: 'SpeechBubbles',
  OwnersWithdrawal: 'Withdrawal',
  Do: 'HappyEmoji',
  Emoji: 'HappyEmoji',
  Balance: 'BarChart',
  Investments: 'BarChart',
  Verified: 'Check',
  FacebookSquare: 'Facebook',
  SoftwareAndWebHosting: 'SoftwareAndHosting',
};

const CANONICAL_TO_ALIASES: Record<string, string[]> = {};
for (const [alias, canonical] of Object.entries(OLD_ICON_NAMES_MAP)) {
  if (!CANONICAL_TO_ALIASES[canonical]) {
    CANONICAL_TO_ALIASES[canonical] = [];
  }
  CANONICAL_TO_ALIASES[canonical].push(alias);
}

type IconEntry = {
  name: string;
  component: IconComponent;
  aliases: string[];
  searchKey: string;
};

const iconEntries: IconEntry[] = Object.entries(AllIcons as Record<string, IconComponent>)
  .filter(([name]) => !OLD_ICON_NAMES_MAP[name])
  .map(([name, component]) => {
    const aliases = CANONICAL_TO_ALIASES[name] ?? [];
    return {
      name,
      component,
      aliases,
      searchKey: [name, ...aliases].join(' ').toLowerCase(),
    };
  })
  .sort((a, b) => a.name.localeCompare(b.name));

const sizeChips = [
  { value: 16, label: '16' },
  { value: 24, label: '24' },
  { value: 32, label: '32' },
];

const themeChips = [
  { value: 'personal', label: 'Personal' },
  { value: 'personal-dark', label: 'Dark' },
  { value: 'bright-green', label: 'Bright Green' },
  { value: 'forest-green', label: 'Forest Green' },
];

type ThemeSelection = {
  theme: 'personal' | 'bright-green' | 'forest-green';
  screenMode: 'light' | 'dark';
};

function resolveTheme(value: string): ThemeSelection {
  if (value === 'personal-dark') return { theme: 'personal', screenMode: 'dark' };
  return { theme: value as ThemeSelection['theme'], screenMode: 'light' };
}

const IconCell = memo(function IconCell({
  name,
  component: Icon,
  aliases,
  iconSize,
  onCopy,
}: {
  name: string;
  component: IconComponent;
  aliases: string[];
  iconSize: 16 | 24 | 32;
  onCopy: (name: string) => void;
}) {
  const [hovered, setHovered] = useState(false);

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        gap: '4px',
        padding: '12px 4px',
        borderRadius: '8px',
        cursor: 'pointer',
        flex: 1,
        background: hovered ? 'var(--color-background-neutral)' : 'transparent',
        transition: 'background 150ms ease',
      }}
      role="button"
      tabIndex={0}
      aria-label={`Copy import for ${name}`}
      onClick={() => onCopy(name)}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onCopy(name);
        }
      }}
    >
      <div style={{ padding: '8px' }}>
        <Icon size={iconSize} />
      </div>
      <span
        style={{
          textAlign: 'center',
          wordBreak: 'break-word',
          fontSize: '12px',
          lineHeight: '1.3',
        }}
      >
        {name}
      </span>
      {aliases.length > 0 && (
        <span
          style={{
            textAlign: 'center',
            wordBreak: 'break-word',
            fontSize: '11px',
            lineHeight: '1.3',
            color: 'var(--color-content-tertiary)',
          }}
        >
          alias: {aliases.join(', ')}
        </span>
      )}
    </div>
  );
});

function IconGallery() {
  const [size, setSize] = useState<ChipValue>(32);
  const [theme, setTheme] = useState<ChipValue>('personal');
  const resolved = resolveTheme(String(theme));

  return (
    <SnackbarProvider timeout={2000}>
      <div style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
        <Toolbar size={size} theme={theme} onSizeChange={setSize} onThemeChange={setTheme} />
        <ThemeProvider theme={resolved.theme} screenMode={resolved.screenMode}>
          <IconGrid size={size} screenMode={resolved.screenMode} />
        </ThemeProvider>
      </div>
    </SnackbarProvider>
  );
}

function Toolbar({
  size,
  onSizeChange,
  theme,
  onThemeChange,
}: {
  size: ChipValue;
  onSizeChange: (v: ChipValue) => void;
  theme: ChipValue;
  onThemeChange: (v: ChipValue) => void;
}) {
  return (
    <div
      style={{
        display: 'flex',
        flexWrap: 'wrap',
        alignItems: 'center',
        gap: '12px',
        padding: '12px 24px',
        borderBottom: '1px solid var(--color-border-neutral)',
        background: 'var(--color-background-elevated)',
        flexShrink: 0,
      }}
    >
      <div style={{ display: 'flex', alignItems: 'center', gap: '12px', paddingRight: '12px' }}>
        <span style={{ fontSize: '13px', fontWeight: 500, whiteSpace: 'nowrap' }}>Size</span>
        <Chips
          chips={sizeChips}
          selected={size}
          aria-label="Icon size"
          onChange={({ selectedValue }) => onSizeChange(selectedValue)}
        />
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
        <span style={{ fontSize: '13px', fontWeight: 500, whiteSpace: 'nowrap' }}>Theme</span>
        <Chips
          chips={themeChips}
          selected={theme}
          aria-label="Theme"
          onChange={({ selectedValue }) => onThemeChange(selectedValue)}
        />
      </div>
    </div>
  );
}

function IconGrid({ size, screenMode }: { size: ChipValue; screenMode: 'light' | 'dark' }) {
  const [search, setSearch] = useState('');
  const gridRef = useRef<HTMLDivElement>(null);
  const countRef = useRef<HTMLSpanElement>(null);
  const createSnackbar = useSnackbar();

  const iconSize = Number(size) as 16 | 24 | 32;

  const handleCopy = useCallback(
    async (name: string) => {
      try {
        await navigator.clipboard.writeText(`<${name} />`);
        createSnackbar({
          text: `Copied`,
          theme: screenMode,
        });
      } catch (error) {
        console.error('Failed to copy icon import to clipboard', error);
        createSnackbar({
          text: `Failed to copy`,
          theme: screenMode,
        });
      }
    },
    [createSnackbar, screenMode],
  );

  const applyFilter = useCallback((query: string) => {
    const grid = gridRef.current;
    if (!grid) return;
    const q = query.toLowerCase();
    let visible = 0;
    const children = grid.children as HTMLCollectionOf<HTMLElement>;
    for (let i = 0; i < children.length; i += 1) {
      const el = children[i];
      const match = !q || (el.dataset.search?.includes(q) ?? false);
      el.style.display = match ? '' : 'none';
      if (match) visible += 1;
    }
    if (countRef.current) {
      countRef.current.textContent = `${visible} icon${visible !== 1 ? 's' : ''}`;
    }
  }, []);

  const handleSearchChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const val = e.target.value;
      setSearch(val);
      applyFilter(val);
    },
    [applyFilter],
  );

  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        gap: '16px',
        padding: '24px',
        background: 'var(--color-background-screen)',
        color: 'var(--color-content-primary)',
        flex: 1,
      }}
    >
      <div style={{ display: 'flex', alignItems: 'center', gap: '16px', flexWrap: 'wrap' }}>
        <div style={{ maxWidth: '320px', flex: 1 }}>
          <SearchInput
            placeholder="Search icons..."
            value={search}
            aria-label="Search icons"
            onChange={handleSearchChange}
          />
        </div>
        <span ref={countRef} style={{ fontSize: '13px', color: 'var(--color-content-secondary)' }}>
          {iconEntries.length} icons
        </span>
        <code>@transferwise/icons@{ICONS_VERSION}</code>
      </div>

      <div style={{ fontSize: '11px', color: 'var(--color-content-tertiary)', lineHeight: '1.8' }}>
        <div>
          Icons package must be imported first:{' '}
          <code>{`import { Bank } from '@transferwise/icons'`}</code>
        </div>
      </div>

      <div
        ref={gridRef}
        style={{
          display: 'grid',
          gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))',
          gap: '12px',
          gridAutoRows: '1fr',
        }}
      >
        {iconEntries.map(({ name, component, aliases, searchKey }) => (
          <div key={name} data-search={searchKey} style={{ display: 'flex' }}>
            <IconCell
              name={name}
              component={component}
              aliases={aliases}
              iconSize={iconSize}
              onCopy={handleCopy}
            />
          </div>
        ))}
      </div>
    </div>
  );
}

const meta: Meta = {
  title: 'Foundations/Icons',
  tags: ['!autodocs', '!manifest'],
  parameters: {
    padding: '0',
  },
};

export default meta;

type Story = StoryObj;

export const Gallery: Story = {
  render: () => <IconGallery />,
};
