import {
  ExclamationTriangleIcon,
  ExternalLinkIcon,
} from '@radix-ui/react-icons';
import { keyframes } from '@stitches/react';
import { GetTwitchLoggedUser, GetTwitchAuthURL } from '@wailsapp/go/main/App';
import { helix } from '@wailsapp/go/models';
import { BrowserOpenURL } from '@wailsapp/runtime/runtime';
import { useSelector } from 'react-redux';
import { useEffect, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { useModule } from '~/lib/react';
import {
  checkTwitchKeys,
  startAuthFlow,
  TwitchCredentials,
} from '~/lib/twitch';
import { languages } from '~/locale/languages';
import { RootState, useAppDispatch } from '~/store';
import apiReducer, { modules } from '~/store/api/reducer';

// @ts-expect-error Asset import
import spinner from '~/assets/icon-logo.svg';

import AlertContent from '../components/AlertContent';
import BrowserLink from '../components/BrowserLink';
import DefinitionTable from '../components/DefinitionTable';
import RevealLink from '../components/utils/RevealLink';
import Channels from '../components/utils/Channels';

import {
  Button,
  ButtonGroup,
  Field,
  InputBox,
  Label,
  lightMode,
  MultiToggle,
  MultiToggleItem,
  PageContainer,
  PasswordInputBox,
  SectionHeader,
  styled,
  TextBlock,
  themes,
} from '../theme';
import { Alert } from '../theme/alert';
import TwitchUserBlock from '../components/TwitchUserBlock';

const Container = styled('div', {
  display: 'flex',
  flexDirection: 'column',
  width: '100%',
});

const TopBanner = styled('div', {
  backgroundColor: '$gray2',
  display: 'flex',
  width: '100%',
  transition: 'all 100ms ease-out',
});

const appear = keyframes({
  '0%': { opacity: 0, transform: 'translate(0, 30px)' },
  '100%': { opacity: 1, transform: 'translate(0, 0)' },
});

const HeroTitle = styled('h1', {
  fontSize: '35pt',
  fontWeight: 200,
  textAlign: 'center',
  padding: 0,
  margin: 0,
  marginBottom: '1em',
  '@media (prefers-reduced-motion: no-preference)': {
    opacity: 0,
    animation: `${appear()} 1s ease-in`,
    animationDelay: '1s',
    animationFillMode: 'forwards',
  },
});

const HeroContainer = styled('div', {
  height: 'calc(100vh - 110px)',
  boxSizing: 'border-box',
  justifyContent: 'center',
  alignItems: 'center',
  display: 'flex',
  flexDirection: 'column',
  width: '100%',
  position: 'relative',
  overflow: 'hidden',
});

const HeroSelector = styled('div', {
  top: '10px',
  left: '10px',
  display: 'flex',
  gap: '1rem',
  position: 'absolute',
  zIndex: '10',
});

const HeroSelectorItem = styled(MultiToggleItem, {
  fontSize: '1rem',
  padding: '5px 8px',
});

const HeroAnimation = styled('div', {
  bottom: '-50px',
  left: '50%',
  position: 'absolute',
});

const HeroContent = styled('div', {
  display: 'flex',
  flexDirection: 'column',
  gap: '1rem',
  maxWidth: '1000px',
  width: '100%',
  padding: '0 3rem',
  '@media (prefers-reduced-motion: no-preference)': {
    opacity: 0,
    animation: `${appear()} 1s ease-in`,
    animationDelay: '1s',
    animationFillMode: 'forwards',
  },
  '& p': { margin: 0, padding: 0 },
});

const fadeOut = keyframes({
  '0%': { transform: 'translate(10px, 0px) rotate(-80deg)' },
  '100%': { opacity: 0, transform: 'translate(-100px, -800px) rotate(30deg)' },
});

const Spinner = styled('img', {
  width: '100px',
  position: 'absolute',
  '@media (prefers-reduced-motion: no-preference)': {
    animation: `${fadeOut()} 2s ease-in`,
    animationFillMode: 'forwards',
  },
});

const StepContainer = styled(PageContainer, {
  display: 'flex',
  flexDirection: 'column',
  paddingTop: '1rem',
  '& p': {
    margin: '1.5rem 0',
  },
});

const ActionContainer = styled('div', {
  flex: 1,
  display: 'flex',
  justifyContent: 'center',
  gap: '1rem',
  paddingTop: '1rem',
});

const StepList = styled('nav', {
  flex: '1',
  display: 'flex',
  alignItems: 'center',
  padding: '0 1rem',
  flexWrap: 'wrap',
  flexDirection: 'row',
  justifyContent: 'flex-start',
  [`.${lightMode} &`]: {
    borderBottom: '1px solid $gray6',
    backgroundColor: '$gray2',
  },
});

const StepName = styled('div', {
  padding: '0.5rem',
  color: '$gray10',
  '&:not(:last-child)::after': {
    color: '$gray10',
    content: '›',
    margin: '0 0 0 1rem',
  },
  display: 'none',
  '@thin': {
    display: 'inherit',
  },
  variants: {
    status: {
      active: {
        color: '$gray12',
        display: 'inherit',

        [`.${lightMode} &`]: {
          fontWeight: '500',
        },
      },
    },
    interaction: {
      clickable: {
        cursor: 'pointer',
      },
    },
  },
});

enum OnboardingSteps {
  Landing = 0,
  TwitchIntegration = 1,
  TwitchEvents = 2,
  Done = 999,
}

const steps = [
  OnboardingSteps.Landing,
  OnboardingSteps.TwitchIntegration,
  OnboardingSteps.TwitchEvents,
  OnboardingSteps.Done,
];

const stepI18n = {
  [OnboardingSteps.Landing]: 'pages.onboarding.sections.landing',
  [OnboardingSteps.TwitchIntegration]:
    'pages.onboarding.sections.twitch-config',
  [OnboardingSteps.TwitchEvents]: 'pages.onboarding.sections.twitch-events',
  [OnboardingSteps.Done]: 'pages.onboarding.sections.done',
};

const maxKeys = languages.reduce(
  (current, it) => Math.max(current, it.keys),
  0,
);

type TestResult = { open: boolean; error?: Error };

const TwitchStepList = styled('ul', {
  lineHeight: '1.5',
  listStyleType: 'none',
  listStylePosition: 'outside',
});
const TwitchStep = styled('li', {
  marginBottom: '0.5rem',
  paddingLeft: '1rem',
  '&::marker': {
    color: '$teal11',
    content: '▧',
    display: 'inline-block',
    marginLeft: '-0.5rem',
  },
});

function TwitchIntegrationStep() {
  const { t } = useTranslation();
  const [httpConfig] = useModule(modules.httpConfig);
  const [twitchConfig, setTwitchConfig] = useModule(modules.twitchConfig);
  const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
  const dispatch = useAppDispatch();
  const [revealClientSecret, setRevealClientSecret] = useState(false);
  const [testing, setTesting] = useState(false);
  const [testResult, setTestResult] = useState<TestResult>({
    open: false,
  });

  const checkCredentials = async () => {
    setTesting(true);
    if (twitchConfig) {
      try {
        await checkTwitchKeys(
          twitchConfig.api_client_id,
          twitchConfig.api_client_secret,
        );
        void dispatch(
          setTwitchConfig({
            ...twitchConfig,
            enabled: true,
          }),
        );
        void dispatch(
          setUiConfig({
            ...uiConfig,
            onboardingStatus: uiConfig.onboardingStatus + 1,
          }),
        );
      } catch (e: unknown) {
        setTestResult({ open: true, error: e as Error });
      }
    }
    setTesting(false);
  };

  function skipTwitch() {
    void dispatch(
      setUiConfig({
        ...uiConfig,
        onboardingStatus:
          steps.findIndex((val) => val === OnboardingSteps.TwitchEvents) + 1,
      }),
    );
  }

  const allFields =
    (twitchConfig?.api_client_id?.length > 0 ?? false) &&
    (twitchConfig?.api_client_secret?.length > 0 ?? false);

  return (
    <form
      onSubmit={(ev) => {
        void dispatch(setTwitchConfig(twitchConfig));
        ev.preventDefault();
      }}
    >
      <TextBlock>{t('pages.onboarding.twitch-p1')}</TextBlock>
      <TwitchStepList>
        <TwitchStep>
          <Trans i18nKey="pages.twitch-settings.apiguide-2">
            {' '}
            <BrowserLink href="https://dev.twitch.tv/console/apps/create">
              https://dev.twitch.tv/console/apps/create
            </BrowserLink>
          </Trans>
        </TwitchStep>
        <TwitchStep>
          {t('pages.twitch-settings.apiguide-3')}

          <DefinitionTable
            entries={{
              [t('pages.twitch-settings.app-oauth-redirect-url')]: `http://${
                httpConfig?.bind.indexOf(':') > 0
                  ? httpConfig.bind
                  : `localhost${httpConfig?.bind ?? ':4337'}`
              }/twitch/callback`,
              [t('pages.twitch-settings.app-category')]: 'Broadcasting Suite',
            }}
          />
        </TwitchStep>
        <TwitchStep>
          <Trans i18nKey="pages.twitch-settings.apiguide-4">
            {'str1 '}
            <b>str2</b>
          </Trans>
        </TwitchStep>
      </TwitchStepList>
      <Field size="fullWidth" css={{ marginTop: '2rem' }}>
        <Label htmlFor="clientid">
          {t('pages.twitch-settings.app-client-id')}
        </Label>
        <InputBox
          type="text"
          id="clientid"
          placeholder={t('pages.twitch-settings.app-client-id')}
          required={true}
          value={twitchConfig?.api_client_id ?? ''}
          onChange={(ev) =>
            dispatch(
              apiReducer.actions.twitchConfigChanged({
                ...twitchConfig,
                api_client_id: ev.target.value,
              }),
            )
          }
        />
      </Field>

      <Field size="fullWidth">
        <Label htmlFor="clientsecret">
          {t('pages.twitch-settings.app-client-secret')}
          <RevealLink
            value={revealClientSecret}
            setter={setRevealClientSecret}
          />
        </Label>
        <PasswordInputBox
          reveal={revealClientSecret}
          id="clientsecret"
          placeholder={t('pages.twitch-settings.app-client-secret')}
          required={true}
          value={twitchConfig?.api_client_secret ?? ''}
          onChange={(ev) =>
            dispatch(
              apiReducer.actions.twitchConfigChanged({
                ...twitchConfig,
                api_client_secret: ev.target.value,
              }),
            )
          }
        />
      </Field>
      <TextBlock>{t('pages.onboarding.twitch-p2')}</TextBlock>
      <ButtonGroup>
        <Button
          type="button"
          variation={'primary'}
          onClick={() => {
            void checkCredentials();
          }}
          disabled={!allFields || testing}
        >
          {t('pages.twitch-settings.test-button')}
        </Button>
        <Button
          type="button"
          onClick={() => {
            skipTwitch();
          }}
        >
          {t('pages.onboarding.twitch-skip')}
        </Button>
      </ButtonGroup>
      <Alert
        defaultOpen={false}
        open={testResult.open}
        onOpenChange={(val: boolean) => {
          setTestResult({ ...testResult, open: val });
        }}
      >
        <AlertContent
          variation={testResult.error ? 'danger' : 'default'}
          description={
            testResult.error
              ? t('pages.twitch-settings.test-failed', {
                  error: testResult.error.message,
                })
              : t('pages.twitch-settings.test-succeeded')
          }
          actionText={t('form-actions.ok')}
          onAction={() => {
            setTestResult({ ...testResult, open: false });
          }}
        />
      </Alert>
    </form>
  );
}

function TwitchEventsStep() {
  const { t } = useTranslation();
  const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
  const dispatch = useAppDispatch();

  const finishStep = async () => {
    await dispatch(
      setUiConfig({
        ...uiConfig,
        onboardingStatus:
          steps.findIndex((val) => val === OnboardingSteps.TwitchEvents) + 1,
      }),
    );
  };

  return (
    <div>
      <TextBlock>{t('pages.onboarding.twitch-ev-p1')}</TextBlock>
      <TextBlock>{t('pages.twitch-settings.events.auth-message')}</TextBlock>
      <ButtonGroup>
        <Button
          variation="primary"
          onClick={() => {
            void startAuthFlow('stream');
          }}
        >
          <ExternalLinkIcon /> {t('pages.twitch-settings.events.auth-button')}
        </Button>
      </ButtonGroup>
      <SectionHeader>
        {t('pages.twitch-settings.events.current-status')}
      </SectionHeader>
      <TwitchUserBlock
        authKey="twitch/auth-keys"
        noUserMessage={t('pages.twitch-settings.events.err-no-user')}
      />
      <TextBlock>{t('pages.onboarding.twitch-ev-p3')}</TextBlock>
      <Button
        variation={'primary'}
        onClick={() => {
          void finishStep();
        }}
      >
        {t('pages.onboarding.twitch-complete')}
      </Button>
    </div>
  );
}

function DoneStep() {
  const { t } = useTranslation();
  const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
  const dispatch = useAppDispatch();

  const done = () => {
    void dispatch(
      setUiConfig({
        ...uiConfig,
        onboardingDone: true,
      }),
    );
  };

  return (
    <div>
      <SectionHeader>{t('pages.onboarding.done-header')}</SectionHeader>
      <TextBlock>{t('pages.onboarding.done-p1')}</TextBlock>
      <TextBlock>{t('pages.onboarding.done-p2')}</TextBlock>
      {Channels}
      <TextBlock>{t('pages.onboarding.done-p3')}</TextBlock>
      <Button variation={'primary'} onClick={() => done()}>
        {t('pages.onboarding.done-button')}
      </Button>
    </div>
  );
}

export default function OnboardingPage() {
  const [t, i18n] = useTranslation();
  const [animationItems, setAnimationItems] = useState<JSX.Element[]>([]);
  const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
  const dispatch = useAppDispatch();
  const navigate = useNavigate();

  const currentStep = steps[uiConfig?.onboardingStatus || 0];
  const landing = currentStep === OnboardingSteps.Landing;

  // Skip onboarding if we've already done it
  const onboardingDone = uiConfig?.onboardingDone;
  useEffect(() => {
    if (onboardingDone) {
      navigate('/');
    }
  }, [onboardingDone]);

  const skip = () => {
    void dispatch(
      setUiConfig({
        ...uiConfig,
        onboardingDone: true,
      }),
    );
  };

  useEffect(() => {
    const spinners = new Array<string>(30).fill(spinner as string);
    setAnimationItems(
      spinners.map((url, i) => (
        <Spinner
          key={i}
          src={url}
          css={{
            marginLeft: `${Math.trunc(Math.random() * 1000) - 500}px`,
            animationDelay: `${(i / spinners.length) * 1000}ms`,
            marginTop: `${Math.trunc(Math.random() * 200 - 50)}px`,
            animationDuration: `${Math.trunc(Math.random() * 1000 + 1000)}ms`,
            width: `${Math.trunc(100 + Math.random() * 100)}px`,
            opacity: `${0.1 + Math.random() * 0.2}`,
            filter: `sepia(100%) saturate(1300%) hue-rotate(${Math.trunc(
              Math.random() * 180,
            )}deg) brightness(120%) contrast(120%)`,
          }}
        />
      )),
    );
  }, []);

  let currentStepBody: JSX.Element = null;
  switch (currentStep) {
    case OnboardingSteps.Landing:
      currentStepBody = (
        <ActionContainer>
          <Button
            css={{ width: '20vw', justifyContent: 'center' }}
            onClick={() => skip()}
          >
            {t('pages.onboarding.skip-button')}
          </Button>
          <Button
            css={{ width: '20vw', justifyContent: 'center' }}
            variation="primary"
            onClick={() => {
              void dispatch(
                setUiConfig({
                  ...uiConfig,
                  onboardingStatus: (uiConfig?.onboardingStatus ?? 0) + 1,
                }),
              );
            }}
          >
            {t('pages.onboarding.welcome-continue-button')}
          </Button>
        </ActionContainer>
      );
      break;
    case OnboardingSteps.TwitchIntegration:
      currentStepBody = <TwitchIntegrationStep />;
      break;
    case OnboardingSteps.TwitchEvents:
      currentStepBody = <TwitchEventsStep />;
      break;
    case OnboardingSteps.Done:
      currentStepBody = <DoneStep />;
      break;
  }

  return (
    <Container>
      <TopBanner>
        {landing ? (
          <HeroContainer>
            <HeroSelector>
              <MultiToggle
                value={uiConfig?.language ?? i18n.resolvedLanguage}
                type="single"
                onValueChange={(newLang) => {
                  void dispatch(
                    setUiConfig({ ...uiConfig, language: newLang }),
                  );
                  localStorage.setItem('language', newLang);
                }}
              >
                {languages.map((lang) => (
                  <HeroSelectorItem
                    key={lang.code}
                    aria-label={lang.name}
                    value={lang.code}
                    title={`${lang.name} ${
                      lang.keys < maxKeys
                        ? `(${t('pages.uiconfig.partial-translation')})`
                        : ''
                    }`}
                  >
                    {lang.name}
                    {lang.keys < maxKeys ? <ExclamationTriangleIcon /> : null}
                  </HeroSelectorItem>
                ))}
              </MultiToggle>

              <MultiToggle
                value={uiConfig?.theme ?? 'dark'}
                type="single"
                onValueChange={(newTheme) => {
                  void dispatch(setUiConfig({ ...uiConfig, theme: newTheme }));
                  localStorage.setItem('theme', newTheme);
                }}
              >
                {themes.map((theme) => (
                  <HeroSelectorItem
                    key={theme}
                    value={theme}
                    aria-label={t(`pages.uiconfig.themes.${theme}`)}
                  >
                    {t(`pages.uiconfig.themes.${theme}`)}
                  </HeroSelectorItem>
                ))}
              </MultiToggle>
            </HeroSelector>
            <HeroAnimation>{animationItems}</HeroAnimation>
            <HeroTitle>{t('pages.onboarding.welcome-header')}</HeroTitle>
            <HeroContent>
              <TextBlock>{t('pages.onboarding.welcome-p1')}</TextBlock>
              <TextBlock>
                <Trans
                  t={t}
                  i18nKey={'pages.onboarding.welcome-guide'}
                  components={{
                    g: (
                      <BrowserLink href="https://strimertul.stream/guide/getting-started/first-time-setup/" />
                    ),
                  }}
                />
              </TextBlock>
              <TextBlock css={{ color: '$gray11' }}>
                {t('pages.onboarding.welcome-p2')}
              </TextBlock>
            </HeroContent>
          </HeroContainer>
        ) : (
          <StepList>
            {steps.map((step) => (
              <StepName
                key={step}
                interaction={step < currentStep ? 'clickable' : undefined}
                status={step === currentStep ? 'active' : undefined}
                onClick={() => {
                  // Can't skip ahead
                  if (step >= currentStep) {
                    return;
                  }
                  void dispatch(
                    setUiConfig({
                      ...uiConfig,
                      onboardingStatus:
                        steps.findIndex((val) => val === step) ?? 0,
                    }),
                  );
                }}
              >
                {t(stepI18n[step])}
              </StepName>
            ))}
          </StepList>
        )}
      </TopBanner>
      <StepContainer>{currentStepBody}</StepContainer>
    </Container>
  );
}
