import {
  CircleIcon,
  ExclamationTriangleIcon,
  InfoCircledIcon,
  UpdateIcon,
} from '@radix-ui/react-icons';
import { Trans, useTranslation } from 'react-i18next';
import {
  EventSubNotification,
  EventSubNotificationType,
  unwrapEvent,
} from '~/lib/eventSub';
import { useLiveKey, useModule } from '~/lib/react';
import { useAppDispatch, useAppSelector } from '~/store';
import { modules } from '~/store/api/reducer';
import * as HoverCard from '@radix-ui/react-hover-card';
import { useEffect, useState } from 'react';
import { main } from '@wailsapp/go/models';
import { GetProblems, GetTwitchAuthURL } from '@wailsapp/go/main/App';
import { BrowserOpenURL } from '@wailsapp/runtime/runtime';
import {
  PageContainer,
  SectionHeader,
  styled,
  TextBlock,
  theme,
  TooltipContent,
} from '../theme';
import BrowserLink from '../components/BrowserLink';
import Scrollbar from '../components/utils/Scrollbar';
import RevealLink from '../components/utils/RevealLink';

interface StreamInfo {
  id: string;
  user_name: string;
  user_login: string;
  game_name: string;
  title: string;
  viewer_count: number;
  started_at: string;
  language: string;
  thumbnail_url: string;
}

const StreamBlock = styled('div', {
  display: 'grid',
  gap: '1rem',
  gridTemplateColumns: '160px 1fr',
});
const StreamTitle = styled('h3', {
  gridRow: 1,
  gridColumn: 2,
  fontWeight: 400,
  margin: 0,
  marginTop: '0.5rem',
});
const StreamInfo = styled('div', {
  gridRow: 2,
  gridColumn: 2,
  fontWeight: 'bold',
  margin: 0,
  marginBottom: '0.5rem',
});
const LiveIndicator = styled('div', {
  gridRow: '1/3',
  gridColumn: 1,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  fontWeight: 'bold',
  backgroundSize: 'cover',
  backgroundPosition: 'center',
});
const Darken = styled(BrowserLink, {
  flex: 1,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  background: 'rgba(0,0,0,0.5)',
  width: '100%',
  height: '100%',
  gap: '0.5rem',
  color: '$red11 !important',
  textDecoration: 'none !important',
  transition: 'all 0.2s ease-in-out',
  '&:hover': {
    opacity: 0.5,
  },
});

const EventListContainer = styled('div', {
  display: 'flex',
  flexDirection: 'column',
  gap: '5px',
});

const TwitchEventContainer = styled('div', {
  background: '$gray3',
  padding: '8px',
  borderRadius: '5px',
  display: 'flex',
  alignItems: 'center',
});

const TwitchEventContent = styled('div', {
  flex: 1,
});
const TwitchEventActions = styled('div', {
  display: 'flex',
  margin: '0 10px',
  '& a': {
    color: '$gray10',
    '&:hover': {
      color: '$gray12',
      cursor: 'pointer',
    },
  },
});
const TwitchEventTime = styled('time', {
  color: '$gray10',
  fontSize: '13px',
});

const UsefulLinksMenu = styled('ul', {
  margin: '0',
  listStyleType: 'square',
  li: {
    padding: '3px',
  },
});

const supportedMessages: EventSubNotificationType[] = [
  EventSubNotificationType.Followed,
  EventSubNotificationType.CustomRewardRedemptionAdded,
  EventSubNotificationType.StreamWentOnline,
  EventSubNotificationType.StreamWentOffline,
  EventSubNotificationType.ChannelUpdated,
  EventSubNotificationType.Raided,
  EventSubNotificationType.Cheered,
  EventSubNotificationType.Subscription,
  EventSubNotificationType.SubscriptionWithMessage,
  EventSubNotificationType.SubscriptionGifted,
];

const eventSubKeyFunction = (ev: EventSubNotification) =>
  `${ev.subscription.type}-${ev.subscription.created_at}-${JSON.stringify(
    ev.event,
  )}`;

function TwitchEvent({ data }: { data: EventSubNotification }) {
  const { t } = useTranslation();
  const client = useAppSelector((state) => state.api.client);

  const replay = () => {
    void client.putJSON(
      `twitch/ev/eventsub-event/${data.subscription.type}`,
      data,
    );
  };

  let content: JSX.Element | string;
  const message = unwrapEvent(data);
  let date = data.date
    ? new Date(data.date)
    : new Date(data.subscription.created_at);
  switch (message.type) {
    case EventSubNotificationType.Followed: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.follow'}
            values={{ name: message.event.user_name }}
            components={{
              n: <b />,
            }}
          />
        </>
      );
      date = new Date(message.event.followed_at);
      break;
    }
    case EventSubNotificationType.CustomRewardRedemptionAdded: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.redemption'}
            values={{
              name: message.event.user_name,
              reward: message.event.reward.title,
            }}
            components={{
              n: <b />,
              r: <b />,
            }}
          />
        </>
      );
      date = new Date(message.event.redeemed_at);
      break;
    }
    case EventSubNotificationType.StreamWentOnline: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.stream-start'}
          />
        </>
      );
      date = new Date(message.event.started_at);
      break;
    }
    case EventSubNotificationType.StreamWentOffline: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.stream-stop'}
          />
        </>
      );
      break;
    }
    case EventSubNotificationType.ChannelUpdated: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.channel-updated'}
          />
        </>
      );
      break;
    }
    case EventSubNotificationType.Raided: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.raided'}
            values={{
              name: message.event.from_broadcaster_user_name,
              viewers: message.event.viewers,
            }}
            components={{
              n: <b />,
              v: <b />,
            }}
          />
        </>
      );
      break;
    }
    case EventSubNotificationType.Cheered: {
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.cheered'}
            values={{
              name: message.event.is_anonymous
                ? t('pages.dashboard.twitch-events.anonymous')
                : message.event.user_name,
              bits: message.event.bits,
            }}
            components={{
              n: <b />,
              b: <b />,
            }}
          />
        </>
      );
      break;
    }
    case EventSubNotificationType.Subscription:
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.subscribed'}
            values={{
              name: message.event.user_name,
              tier: message.event.tier.substring(0, 1),
            }}
            components={{
              n: <b />,
              t: <></>,
            }}
          />
        </>
      );
      break;
    case EventSubNotificationType.SubscriptionWithMessage:
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.subscribed-multi'}
            values={{
              name: message.event.user_name,
              months: message.event.cumulative_months,
              tier: message.event.tier.substring(0, 1),
            }}
            components={{
              n: <b />,
              m: <></>,
              t: <></>,
            }}
          />
        </>
      );
      break;
    case EventSubNotificationType.SubscriptionGifted:
      content = (
        <>
          <Trans
            t={t}
            i18nKey={'pages.dashboard.twitch-events.events.subscrition-gift'}
            values={{
              count: message.event.total,
              name: message.event.is_anonymous
                ? t('pages.dashboard.twitch-events.anonymous')
                : message.event.user_name,
              tier: message.event.tier.substring(0, 1),
            }}
            components={{
              n: <b />,
              c: <></>,
              t: <></>,
            }}
          />
        </>
      );
      break;
    default:
      content = <small>{message.type}</small>;
  }

  return (
    <TwitchEventContainer>
      <TwitchEventContent>{content}</TwitchEventContent>
      <TwitchEventTime
        title={date?.toLocaleString()}
        dateTime={message.subscription.created_at}
      >
        {date?.toLocaleTimeString()}
      </TwitchEventTime>
      <TwitchEventActions>
        <a
          aria-label={t('pages.dashboard.twitch-events.replay')}
          title={t('pages.dashboard.twitch-events.replay')}
          onClick={() => {
            replay();
          }}
        >
          <UpdateIcon />
        </a>
      </TwitchEventActions>
    </TwitchEventContainer>
  );
}

function TwitchEventLog({ events }: { events: EventSubNotification[] }) {
  const { t } = useTranslation();
  return (
    <>
      <HoverCard.Root>
        <HoverCard.Trigger asChild>
          <SectionHeader>
            {t('pages.dashboard.twitch-events.header')}
            <a style={{ marginLeft: '10px' }}>
              <InfoCircledIcon />
            </a>
          </SectionHeader>
        </HoverCard.Trigger>
        <HoverCard.Portal>
          <TooltipContent>
            {t('pages.dashboard.twitch-events.warning')}
          </TooltipContent>
        </HoverCard.Portal>
      </HoverCard.Root>
      <Scrollbar vertical={true} viewport={{ maxHeight: '250px' }}>
        <EventListContainer>
          {events
            .filter((ev) => supportedMessages.includes(ev.subscription.type))
            .sort((a, b) =>
              a.date && b.date
                ? Date.parse(b.date) - Date.parse(a.date)
                : Date.parse(b.subscription.created_at) -
                  Date.parse(a.subscription.created_at),
            )
            .map((ev) => (
              <TwitchEvent key={eventSubKeyFunction(ev)} data={ev} />
            ))}
        </EventListContainer>
      </Scrollbar>
    </>
  );
}

function TwitchStreamStatus({ info }: { info: StreamInfo }) {
  const { t } = useTranslation();
  const [uiConfig, setUiConfig] = useModule(modules.uiConfig);
  const dispatch = useAppDispatch();
  return (
    <StreamBlock>
      <LiveIndicator
        css={{
          backgroundImage: `url(${info.thumbnail_url
            .replace('{width}', '160')
            .replace('{height}', '90')})`,
        }}
      >
        <Darken target="_blank" href={`https://twitch.tv/${info.user_login}`}>
          <CircleIcon /> {t('pages.dashboard.live')}
        </Darken>
      </LiveIndicator>
      <StreamTitle>{info.title}</StreamTitle>
      <StreamInfo>
        {info.game_name} -{' '}
        {t('pages.dashboard.x-viewers', {
          num: uiConfig.hideViewers ? '...' : `${info.viewer_count}`,
        })}{' '}
        <RevealLink
          value={!uiConfig.hideViewers}
          setter={(newVal) => {
            void dispatch(setUiConfig({ ...uiConfig, hideViewers: !newVal }));
          }}
        />
      </StreamInfo>
    </StreamBlock>
  );
}

function TwitchSection() {
  const { t } = useTranslation();
  const twitchInfo = useLiveKey<StreamInfo[]>('twitch/stream-info');
  const kv = useAppSelector((state) => state.api.client);
  const [twitchEvents, setTwitchEvents] = useState<EventSubNotification[]>([]);

  const keyfn = (ev: EventSubNotification) => JSON.stringify(ev);

  const addTwitchEvents = (events: EventSubNotification[]) => {
    setTwitchEvents((currentEvents) => {
      const allEvents = currentEvents.concat(events);
      const eventKeys = allEvents.map(keyfn);

      // Clean up duplicates before setting to state
      const updatedEvents = allEvents.filter(
        (ev, pos) => eventKeys.indexOf(keyfn(ev)) === pos,
      );

      return updatedEvents;
    });
  };

  const loadRecentEvents = async () => {
    const keymap = await kv.getKeysByPrefix('twitch/eventsub-history/');
    const events = Object.values(keymap)
      .map((value) => JSON.parse(value) as EventSubNotification[])
      .flat();

    addTwitchEvents(events);
  };

  useEffect(() => {
    void loadRecentEvents();

    const onKeyChange = (value: string) => {
      const event = JSON.parse(value) as EventSubNotification;
      if (!supportedMessages.includes(event.subscription.type)) {
        return;
      }
      void addTwitchEvents([event]);
    };

    void kv.subscribePrefix('twitch/ev/eventsub-event/', onKeyChange);

    return () => {
      void kv.unsubscribePrefix('twitch/ev/eventsub-event/', onKeyChange);
    };
  }, []);

  return (
    <>
      <SectionHeader spacing="none">
        {t('pages.dashboard.twitch-status')}
      </SectionHeader>
      {twitchInfo && twitchInfo.length > 0 ? (
        <TwitchStreamStatus info={twitchInfo[0]} />
      ) : (
        <TextBlock>{t('pages.dashboard.not-live')}</TextBlock>
      )}
      {twitchEvents ? <TwitchEventLog events={twitchEvents} /> : null}
    </>
  );
}

const ProblemBlock = styled('div', {
  border: '2px solid $gray6',
  padding: '0.5rem 1rem',
  borderRadius: theme.borderRadius.toolbar,
  variants: {
    severity: {
      warn: {
        borderColor: '$yellow6',
        backgroundColor: '$yellow3',
        color: '$yellow12',
        svg: {
          color: '$yellow11',
        },
      },
    },
  },
  display: 'flex',
  gap: '1rem',
  alignItems: 'center',
  lineHeight: '1.4',
  svg: {
    marginTop: '0.25rem',
  },
  a: {
    cursor: 'pointer',
  },
});

function ProblemList() {
  const [problems, setProblems] = useState<main.Problem[]>([]);
  const { t } = useTranslation();
  const kv = useAppSelector((state) => state.api.client);

  useEffect(() => {
    void GetProblems().then(setProblems);
  }, []);

  const reauthenticate = async () => {
    // Wait for re-auth so we can clear the banner
    const onKeyChange = () => {
      void GetProblems().then(setProblems);
      void kv.unsubscribeKey('twitch/auth-keys', onKeyChange);
    };
    void kv.subscribeKey('twitch/auth-keys', onKeyChange);

    const url = await GetTwitchAuthURL('stream');
    BrowserOpenURL(url);
  };

  return (
    <>
      {problems.map((p) => {
        switch (p.id) {
          case 'twitch:eventsub_scope':
            return (
              <ProblemBlock severity="warn">
                <ExclamationTriangleIcon
                  style={{ width: 'auto', minWidth: '40px', height: '40px' }}
                />
                <header>
                  <Trans
                    t={t}
                    i18nKey={'pages.dashboard.problems.eventsub-scope'}
                    components={{
                      a: (
                        <a
                          onClick={() => {
                            void reauthenticate();
                          }}
                        ></a>
                      ),
                    }}
                  />
                </header>
              </ProblemBlock>
            );
          default:
            return null;
        }
      })}
    </>
  );
}

export default function Dashboard(): React.ReactElement {
  const { t } = useTranslation();
  return (
    <PageContainer>
      <ProblemList />
      <TwitchSection />
      <SectionHeader>{t('pages.dashboard.quick-links')}</SectionHeader>
      <UsefulLinksMenu>
        <li>
          <BrowserLink href="https://strimertul.stream/guide/">
            {t('pages.dashboard.link-user-guide')}
          </BrowserLink>
        </li>
        <li>
          <BrowserLink href="https://strimertul.stream/api/v31/">
            {t('pages.dashboard.link-api')}
          </BrowserLink>
        </li>
      </UsefulLinksMenu>
    </PageContainer>
  );
}
