import { PlusIcon } from '@radix-ui/react-icons';
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useModule } from '~/lib/react';
import { useAppDispatch } from '~/store';
import { modules } from '~/store/api/reducer';
import {
  accessLevels,
  AccessLevelType,
  ReplyType,
  TwitchChatCustomCommand,
} from '~/store/api/types';
import { TestCommandTemplate } from '@wailsapp/go/main/App';
import AlertContent from '../components/AlertContent';
import DialogContent from '../components/DialogContent';
import {
  Button,
  ComboBox,
  Dialog,
  DialogActions,
  DialogClose,
  Field,
  FieldNote,
  FlexRow,
  InputBox,
  Label,
  MultiButton,
  MultiToggle,
  MultiToggleItem,
  NoneText,
  PageContainer,
  PageHeader,
  PageTitle,
  styled,
  Textarea,
  TextBlock,
} from '../theme';
import { Alert, AlertTrigger } from '../theme/alert';

const CommandList = styled('div', { marginTop: '1rem' });
const CommandItemContainer = styled('article', {
  backgroundColor: '$gray2',
  margin: '0.5rem 0',
  padding: '0.5rem',
  borderLeft: '5px solid $teal8',
  borderRadius: '0.25rem',
  borderBottom: '1px solid $gray4',
  transition: 'all 50ms',
  '&:hover': {
    backgroundColor: '$gray3',
  },
  variants: {
    status: {
      enabled: {},
      disabled: {
        borderLeftColor: '$red6',
        backgroundColor: '$gray3',
        color: '$gray10',
      },
    },
  },
});
const CommandHeader = styled('header', {
  display: 'flex',
  gap: '0.5rem',
  alignItems: 'center',
  marginBottom: '0.4rem',
});
const CommandName = styled('span', {
  color: '$teal10',
  fontWeight: 'bold',
  variants: {
    status: {
      enabled: {},
      disabled: {
        color: '$gray9',
      },
    },
  },
});
const CommandDescription = styled('span', {
  display: 'flex',
  alignContent: 'baseline',
  gap: '0.25rem',
  flex: 1,
});
const CommandActions = styled('div', {
  display: 'flex',
  alignItems: 'center',
  gap: '0.25rem',
});
const CommandText = styled('div', {
  fontFamily: 'Space Mono',
  fontSize: '10pt',
  margin: '-0.5rem',
  marginTop: '0',
  padding: '0.5rem',
  backgroundColor: '$gray4',
  lineHeight: '1.2rem',
});
const CommandType = styled('div', {
  fontFamily: 'Inter',
  display: 'inline-flex',
  backgroundColor: '$gray2',
  fontSize: '8pt',
  textTransform: 'uppercase',
  alignItems: 'center',
  padding: '0.2rem 0.3rem',
  borderRadius: '0.4rem',
  marginRight: '0.5rem',
  height: '1.2rem',
  variants: {
    type: {
      chat: {
        display: 'none',
      },
      whisper: {
        backgroundColor: '$amber4',
        color: '$amber9',
      },
      announce: {
        backgroundColor: '$crimson4',
        color: '$crimson11',
      },
      reply: {
        backgroundColor: '$gray2',
        color: '$gray12',
      },
    },
  },
});
const ACLIndicator = styled('span', {
  fontFamily: 'Space Mono',
  fontSize: '10pt',
  marginRight: '0.5rem',
});

interface CommandItemProps {
  name: string;
  item: TwitchChatCustomCommand;
  onToggle?: () => void;
  onEdit?: () => void;
  onDelete?: () => void;
}

function CommandItem({
  name,
  item,
  onToggle,
  onEdit,
  onDelete,
}: CommandItemProps): React.ReactElement {
  const { t } = useTranslation();

  return (
    <CommandItemContainer status={item.enabled ? 'enabled' : 'disabled'}>
      <CommandHeader>
        <CommandName status={item.enabled ? 'enabled' : 'disabled'}>
          {name}
        </CommandName>
        <CommandDescription>{item.description}</CommandDescription>
        <CommandActions>
          {item.access_level !== 'everyone' && (
            <ACLIndicator>
              {t(`pages.botcommands.acl.${item.access_level}`)}
              {item.access_level !== 'streamer' && '+'}
            </ACLIndicator>
          )}
          <MultiButton>
            <Button
              styling="multi"
              size="small"
              onClick={() => (onToggle ? onToggle() : null)}
            >
              {t(item.enabled ? 'form-actions.disable' : 'form-actions.enable')}
            </Button>
            <Button
              styling="multi"
              size="small"
              onClick={() => (onEdit ? onEdit() : null)}
            >
              {t('form-actions.edit')}
            </Button>
            <Alert>
              <AlertTrigger asChild>
                <Button styling="multi" size="small">
                  {t('form-actions.delete')}
                </Button>
              </AlertTrigger>
              <AlertContent
                variation="danger"
                title={t('pages.botcommands.remove-command-title', { name })}
                description={t('form-actions.warning-delete')}
                actionText={t('form-actions.delete')}
                actionButtonProps={{ variation: 'danger' }}
                showCancel={true}
                onAction={() => (onDelete ? onDelete() : null)}
              />
            </Alert>
          </MultiButton>
        </CommandActions>
      </CommandHeader>
      <CommandText>
        <CommandType type={item.response_type ?? 'chat'}>
          {t(`pages.botcommands.response-types.${item.response_type}`)}
        </CommandType>
        {item.response}
      </CommandText>
    </CommandItemContainer>
  );
}

type DialogPrompt =
  | { kind: 'new' }
  | { kind: 'edit'; name: string; item: TwitchChatCustomCommand };

function CommandDialog({
  kind,
  name,
  item,
  onSubmit,
}: {
  kind: 'new' | 'edit';
  name?: string;
  item?: TwitchChatCustomCommand;
  onSubmit?: (name: string, item: TwitchChatCustomCommand) => void;
}) {
  const [commands] = useModule(modules.twitchChatCommands);
  const [commandName, setCommandName] = useState(name ?? '');
  const [description, setDescription] = useState(item?.description ?? '');
  const [responseType, setResponseType] = useState(
    item?.response_type ?? 'chat',
  );
  const [response, setResponse] = useState(item?.response ?? '');
  const responseRef = useRef<HTMLTextAreaElement>(null);
  const [accessLevel, setAccessLevel] = useState(
    item?.access_level ?? 'everyone',
  );
  const [responseError, setResponseError] = useState<string | null>(null);
  const { t } = useTranslation();
  const replyTypes: ReplyType[] = ['chat', 'reply', 'whisper', 'announce'];

  return (
    <DialogContent
      title={t(`pages.botcommands.command-header-${kind}`)}
      closeButton={true}
    >
      <form
        onSubmit={(e) => {
          if (!(e.target as HTMLFormElement).checkValidity()) {
            return false;
          }
          e.preventDefault();
          void (async () => {
            try {
              await TestCommandTemplate(response);
              if (onSubmit) {
                onSubmit(commandName, {
                  ...item,
                  description,
                  response,
                  response_type: responseType,
                  access_level: accessLevel,
                });
              }
            } catch (error: unknown) {
              setResponseError(error as string);
              responseRef.current?.setCustomValidity(
                t('pages.botcommands.command-invalid-format'),
              );
            }
          })();
        }}
      >
        <Field spacing="narrow" size="fullWidth">
          <Label htmlFor="command-name">
            {t('pages.botcommands.command-name')}
          </Label>
          <InputBox
            id="command-name"
            value={commandName}
            required={true}
            onChange={(e) => {
              setCommandName(e.target.value);
              // If command name is different but matches another defined command, set as invalid
              if (e.target.value !== name && e.target.value in commands) {
                (e.target as HTMLInputElement).setCustomValidity(
                  t('pages.botcommands.command-already-in-use'),
                );
              } else {
                (e.target as HTMLInputElement).setCustomValidity('');
              }
            }}
            placeholder={t('pages.botcommands.command-name-placeholder')}
          />
        </Field>
        <Field spacing="narrow" size="fullWidth">
          <Label htmlFor="command-description">
            {t('pages.botcommands.command-desc')}
          </Label>
          <InputBox
            id="command-description"
            value={description}
            onChange={(e) => setDescription(e.target.value)}
            placeholder={t('pages.botcommands.command-desc-placeholder')}
          />
        </Field>
        <Field spacing="narrow" size="fullWidth">
          <Label htmlFor="command-response">
            {t('pages.botcommands.command-response')}
            <MultiToggle
              css={{ marginLeft: '0.5rem' }}
              value={responseType}
              type="single"
              onValueChange={(newType) => {
                setResponseType(newType as ReplyType);
              }}
            >
              {replyTypes.map((replyType) => (
                <MultiToggleItem size="small" key={replyType} value={replyType}>
                  {t(`pages.botcommands.response-types.${replyType}`)}
                </MultiToggleItem>
              ))}
            </MultiToggle>
          </Label>
          <Textarea
            value={response}
            required={true}
            onChange={(e) => {
              responseRef.current?.setCustomValidity('');
              setResponse(e.target.value);
            }}
            id="command-response"
            ref={responseRef}
            placeholder={t('pages.botcommands.command-response-placeholder')}
          >
            {item?.response}
          </Textarea>
          {responseError && (
            <FieldNote
              css={{
                color: '$red10',
              }}
            >
              {responseError}
            </FieldNote>
          )}
        </Field>
        <Field spacing="narrow" size="fullWidth">
          <Label htmlFor="command-acl">
            {t('pages.botcommands.command-acl')}
          </Label>
          <ComboBox
            id="command-acl"
            value={accessLevel}
            onChange={(e) => setAccessLevel(e.target.value as AccessLevelType)}
          >
            {accessLevels.map((level) => (
              <option key={level} value={level}>
                {t(`pages.botcommands.acl.${level}`)}
              </option>
            ))}
          </ComboBox>
          <FieldNote>{t('pages.botcommands.command-acl-help')}</FieldNote>
        </Field>
        <DialogActions>
          <Button variation="primary">
            {kind === 'new' ? t('form-actions.create') : t('form-actions.edit')}
          </Button>
          <DialogClose asChild>
            <Button type="button">{t('form-actions.cancel')}</Button>
          </DialogClose>
        </DialogActions>
      </form>
    </DialogContent>
  );
}

export default function TwitchChatCommandsPage(): React.ReactElement {
  const [commands, setCommands] = useModule(modules.twitchChatCommands);
  const [filter, setFilter] = useState('');
  const [activeDialog, setActiveDialog] = useState<DialogPrompt>(null);
  const { t } = useTranslation();
  const dispatch = useAppDispatch();

  const filterLC = filter.toLowerCase();

  const setCommand = (newName: string, data: TwitchChatCustomCommand): void => {
    switch (activeDialog.kind) {
      case 'new':
        void dispatch(
          setCommands({
            ...commands,
            [newName]: {
              ...data,
              enabled: true,
            },
          }),
        );
        break;
      case 'edit': {
        const oldName = activeDialog.name;
        void dispatch(
          setCommands({
            ...commands,
            [oldName]: undefined,
            [newName]: data,
          }),
        );
        break;
      }
    }
    setActiveDialog(null);
  };

  const deleteCommand = (cmd: string): void => {
    void dispatch(
      setCommands({
        ...commands,
        [cmd]: undefined,
      }),
    );
  };

  const toggleCommand = (cmd: string): void => {
    void dispatch(
      setCommands({
        ...commands,
        [cmd]: {
          ...commands[cmd],
          enabled: !commands[cmd].enabled,
        },
      }),
    );
  };

  return (
    <PageContainer>
      <PageHeader>
        <PageTitle>{t('pages.botcommands.title')}</PageTitle>
        <TextBlock>{t('pages.botcommands.desc')}</TextBlock>
      </PageHeader>

      <FlexRow spacing="1" align="left">
        <Button
          variation="primary"
          onClick={() => setActiveDialog({ kind: 'new' })}
        >
          <PlusIcon /> {t('pages.botcommands.add-button')}
        </Button>

        <InputBox
          css={{ flex: 1 }}
          placeholder={t('pages.botcommands.search-placeholder')}
          value={filter}
          onChange={(e) => setFilter(e.target.value)}
        />
      </FlexRow>
      <CommandList>
        {commands ? (
          Object.keys(commands ?? {})
            ?.filter(
              (cmd) =>
                cmd.toLowerCase().includes(filterLC) ||
                commands[cmd].description.toLowerCase().includes(filterLC),
            )
            .sort()
            .map((cmd) => (
              <CommandItem
                key={cmd}
                name={cmd}
                item={commands[cmd]}
                onToggle={() => toggleCommand(cmd)}
                onEdit={() =>
                  setActiveDialog({
                    kind: 'edit',
                    name: cmd,
                    item: commands[cmd],
                  })
                }
                onDelete={() => deleteCommand(cmd)}
              />
            ))
        ) : (
          <NoneText>{t('pages.botcommands.no-commands')}</NoneText>
        )}
      </CommandList>

      <Dialog
        open={!!activeDialog}
        onOpenChange={(open) => {
          if (!open) {
            // Reset dialog status on dialog close
            setActiveDialog(null);
          }
        }}
      >
        {activeDialog && (
          <CommandDialog
            {...activeDialog}
            onSubmit={(name, data) => setCommand(name, data)}
          />
        )}
      </Dialog>
    </PageContainer>
  );
}
