import { useMemo } from 'react';
import { Box, Flex } from '@chakra-ui/react';
import { TabsLayout, PreviewTab, CodeTab } from '../../components/common/TabsLayout';

import CodeExample from '../../components/code/CodeExample';
import Dependencies from '../../components/code/Dependencies';
import PropTable from '../../components/common/Preview/PropTable';
import RefreshButton from '../../components/common/Preview/RefreshButton';
import Customize from '../../components/common/Preview/Customize';
import PreviewColorPickerCustom from '../../components/common/Preview/PreviewColorPickerCustom';
import PreviewSelect from '../../components/common/Preview/PreviewSelect';
import PreviewSlider from '../../components/common/Preview/PreviewSlider';
import PreviewSwitch from '../../components/common/Preview/PreviewSwitch';

import useForceRerender from '../../hooks/useForceRerender';
import useComponentProps from '../../hooks/useComponentProps';
import { ComponentPropsProvider } from '../../components/context/ComponentPropsContext';
import { useColorModeValue } from '../../components/setup/color-mode';

import DecryptedText from '../../content/TextAnimations/DecryptedText/DecryptedText';
import { decryptedText } from '../../constants/code/TextAnimations/decryptedTextCode';

const CHARACTER_SETS = {
  letters: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()_+',
  hex: '0123456789ABCDEF',
  binary: '01',
  blocks: '█▓▒░',
  symbols: '!<>-_/[]{}=+*^?#'
};

const LOOK = {
  scrambleColor: '',
  effect: 'scramble',
  animateOn: 'inViewHover',
  speed: 60,
  maxIterations: 10,
  sequential: true,
  revealDirection: 'start',
  hoverRadius: 100,
  loop: false,
  loopDelay: 2000,
  characters: CHARACTER_SETS.letters,
  useOriginalCharsOnly: false
};

const PRESETS = {
  classic: { ...LOOK },
  terminal: {
    ...LOOK,
    scrambleColor: '#2bb673',
    revealDirection: 'random',
    speed: 40,
    hoverRadius: 70,
    characters: CHARACTER_SETS.hex
  },
  flip: {
    ...LOOK,
    effect: 'flip',
    revealDirection: 'center',
    speed: 70,
    hoverRadius: 0
  },
  loop: {
    ...LOOK,
    effect: 'fade',
    animateOn: 'view',
    speed: 45,
    hoverRadius: 0,
    loop: true,
    loopDelay: 1800,
    characters: CHARACTER_SETS.symbols
  }
};

const DEFAULT_PROPS = {
  preset: 'classic',
  ...LOOK,
  clickMode: 'once'
};

const PRESET_OPTIONS = [
  { value: 'classic', label: 'Classic' },
  { value: 'terminal', label: 'Terminal' },
  { value: 'flip', label: 'Flip' },
  { value: 'loop', label: 'Loop' }
];

const EFFECT_OPTIONS = [
  { label: 'Scramble', value: 'scramble' },
  { label: 'Flip', value: 'flip' },
  { label: 'Fade', value: 'fade' }
];

const ANIMATE_OPTIONS = [
  { label: 'View', value: 'view' },
  { label: 'Hover', value: 'hover' },
  { label: 'View & Hover', value: 'inViewHover' },
  { label: 'Click', value: 'click' }
];

const CLICK_OPTIONS = [
  { label: 'Once', value: 'once' },
  { label: 'Toggle', value: 'toggle' }
];

const DIRECTION_OPTIONS = [
  { label: 'Start', value: 'start' },
  { label: 'End', value: 'end' },
  { label: 'Center', value: 'center' },
  { label: 'Random', value: 'random' }
];

const CHARACTER_OPTIONS = [
  { label: 'Letters', value: CHARACTER_SETS.letters },
  { label: 'Hex', value: CHARACTER_SETS.hex },
  { label: 'Binary', value: CHARACTER_SETS.binary },
  { label: 'Blocks', value: CHARACTER_SETS.blocks },
  { label: 'Symbols', value: CHARACTER_SETS.symbols }
];

const propData = [
  {
    name: 'text',
    type: 'string',
    default: '-',
    description: 'The text to decrypt.'
  },
  {
    name: 'effect',
    type: "'scramble' | 'flip' | 'fade'",
    default: "'scramble'",
    description:
      'How letters decode. Scramble swaps glyphs in place, flip rolls each glyph in vertically and lands the real letter with a soft bounce, fade keeps scrambled glyphs faint and brightens them as they resolve.'
  },
  {
    name: 'scrambleColor',
    type: 'string',
    default: "''",
    description:
      'Color of letters while they are scrambled. Each letter eases back to the text color as it decodes. Leave empty to keep the text color.'
  },
  {
    name: 'speed',
    type: 'number',
    default: '50',
    description: 'Time between steps, in ms.'
  },
  {
    name: 'maxIterations',
    type: 'number',
    default: '10',
    description:
      'How many steps the letters scramble before settling when not sequential. Also sets how long letters keep scrambling after the cursor passes with hoverRadius.'
  },
  {
    name: 'sequential',
    type: 'boolean',
    default: 'false',
    description: 'Reveals one letter per step instead of all at once.'
  },
  {
    name: 'revealDirection',
    type: "'start' | 'end' | 'center' | 'random'",
    default: "'start'",
    description: 'Where the sequential reveal starts. Random uncovers the letters in a shuffled order.'
  },
  {
    name: 'useOriginalCharsOnly',
    type: 'boolean',
    default: 'false',
    description: 'Scrambles using only the letters already in the text.'
  },
  {
    name: 'characters',
    type: 'string',
    default: "'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()_+'",
    description: 'The pool of characters used while scrambling.'
  },
  {
    name: 'animateOn',
    type: "'view' | 'hover' | 'inViewHover' | 'click'",
    default: "'hover'",
    description: 'What starts the decryption. View and click start scrambled, so the real text never flashes first.'
  },
  {
    name: 'clickMode',
    type: "'once' | 'toggle'",
    default: "'once'",
    description: "With click, decrypt once or toggle back and forth. Only applies when animateOn is 'click'."
  },
  {
    name: 'hoverRadius',
    type: 'number',
    default: '0',
    description:
      'With hover, only letters within this many px of the cursor scramble, fading out toward the edge, and they settle once it passes. 0 decrypts the whole text on hover.'
  },
  {
    name: 'loop',
    type: 'boolean',
    default: 'false',
    description: 'After decrypting, holds, scrambles the text again and repeats. Pauses while off screen.'
  },
  {
    name: 'loopDelay',
    type: 'number',
    default: '2000',
    description: 'How long the decrypted text holds before scrambling again when looping, in ms.'
  },
  {
    name: 'className',
    type: 'string',
    default: "''",
    description: 'Class names for revealed letters.'
  },
  {
    name: 'parentClassName',
    type: 'string',
    default: "''",
    description: 'Class names for the wrapper around all letters.'
  },
  {
    name: 'encryptedClassName',
    type: 'string',
    default: "''",
    description: 'Class names for letters that are still scrambled.'
  }
];

const DecryptedTextDemo = () => {
  const { props, defaultProps, updateProp, updateProps, resetProps, hasChanges } = useComponentProps(DEFAULT_PROPS);
  const { preset, scrambleColor, ...settings } = props;
  const [key, forceRerender] = useForceRerender();
  const ink = useColorModeValue('#18181b', '#ffffff');
  const hoverable = settings.animateOn === 'hover' || settings.animateOn === 'inViewHover';

  const change = (name, value) => {
    updateProp(name, value);
    forceRerender();
  };

  const applyPreset = value => {
    const base = Object.fromEntries(Object.keys(LOOK).map(name => [name, defaultProps[name]]));
    updateProps({ ...base, ...PRESETS[value], preset: value });
    forceRerender();
  };

  const computedProps = useMemo(() => (scrambleColor ? { scrambleColor } : {}), [scrambleColor]);

  return (
    <ComponentPropsProvider
      props={props}
      defaultProps={DEFAULT_PROPS}
      resetProps={resetProps}
      hasChanges={hasChanges}
      demoOnlyProps={['preset', 'scrambleColor']}
      computedProps={computedProps}
    >
      <TabsLayout>
        <PreviewTab>
          <Box position="relative" py={{ md: 6, sm: 4 }} className="demo-container" h={400} overflow="hidden">
            <RefreshButton onClick={forceRerender} />

            <Flex width="80%" px=".5em">
              <DecryptedText
                key={key}
                text="Hacking into the mainframe..."
                parentClassName="decrypted-text"
                {...settings}
                scrambleColor={scrambleColor}
              />
            </Flex>
          </Box>

          <Customize>
            <PreviewSelect title="Preset" options={PRESET_OPTIONS} value={preset} onChange={applyPreset} />
            <PreviewColorPickerCustom
              title="Scramble Color"
              color={scrambleColor || ink}
              onChange={value => updateProp('scrambleColor', value)}
            />
            <PreviewSelect
              title="Effect"
              options={EFFECT_OPTIONS}
              value={settings.effect}
              onChange={value => change('effect', value)}
            />
            <PreviewSelect
              title="Animate On"
              options={ANIMATE_OPTIONS}
              value={settings.animateOn}
              onChange={value => change('animateOn', value)}
            />
            <PreviewSelect
              title="Click Mode"
              options={CLICK_OPTIONS}
              value={settings.clickMode}
              isDisabled={settings.animateOn !== 'click'}
              onChange={value => change('clickMode', value)}
            />
            <PreviewSelect
              title="Direction"
              options={DIRECTION_OPTIONS}
              value={settings.revealDirection}
              isDisabled={!settings.sequential}
              onChange={value => change('revealDirection', value)}
            />
            <PreviewSlider
              title="Speed"
              min={20}
              max={200}
              step={5}
              value={settings.speed}
              valueUnit="ms"
              onChange={value => change('speed', value)}
            />
            <PreviewSlider
              title="Iterations"
              min={1}
              max={40}
              step={1}
              value={settings.maxIterations}
              isDisabled={settings.sequential && !(hoverable && settings.hoverRadius > 0)}
              onChange={value => change('maxIterations', value)}
            />
            <PreviewSlider
              title="Hover Radius"
              min={0}
              max={240}
              step={10}
              value={settings.hoverRadius}
              valueUnit="px"
              isDisabled={!hoverable}
              onChange={value => change('hoverRadius', value)}
            />
            <PreviewSwitch title="Loop" isChecked={settings.loop} onChange={value => change('loop', value)} />
            <PreviewSlider
              title="Loop Delay"
              min={400}
              max={5000}
              step={100}
              value={settings.loopDelay}
              valueUnit="ms"
              isDisabled={!settings.loop}
              onChange={value => change('loopDelay', value)}
            />
            <PreviewSwitch
              title="Sequential"
              isChecked={settings.sequential}
              onChange={value => change('sequential', value)}
            />
            <PreviewSelect
              title="Characters"
              options={CHARACTER_OPTIONS}
              value={settings.characters}
              isDisabled={settings.useOriginalCharsOnly}
              onChange={value => change('characters', value)}
            />
            <PreviewSwitch
              title="Original Chars"
              isChecked={settings.useOriginalCharsOnly}
              onChange={value => change('useOriginalCharsOnly', value)}
            />
          </Customize>

          <PropTable data={propData} />
          <Dependencies dependencyList={['motion']} />
        </PreviewTab>

        <CodeTab>
          <CodeExample codeObject={decryptedText} componentName="DecryptedText" />
        </CodeTab>
      </TabsLayout>
    </ComponentPropsProvider>
  );
};

export default DecryptedTextDemo;