import {
  Alert,
  Box,
  Button,
  Code,
  Container,
  Divider,
  Input,
  LoadingOverlay,
  Modal,
  ScrollArea,
  Tabs,
  Text,
} from '@mantine/core';
import { IconInfoCircle, IconRefresh, IconBrandGithub } from '@tabler/icons-react';
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued';
import React, { useEffect, useMemo, useState } from 'react';
import { modals } from '@mantine/modals';
import { notifications } from '@mantine/notifications';
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import { getEchoHeaders } from '@/src/components/utils';
import { useSegment } from '@/src/app/segment.provider';
import { CodeHighlightTabs } from '@mantine/code-highlight';

export function SyncModal({ connected }: { connected: boolean }) {
  const { track } = useSegment();
  const [diffMode, setDiffMode] = useState(false);
  const [opened, { open, close }] = useDisclosure(false);

  async function showModal() {
    setDiffMode(false);
    open();

    track('Sync Modal Opened');
  }

  return (
    <>
      <Button
        onClick={showModal}
        leftSection={<IconRefresh size={14} />}
        variant="default"
        size="xs"
        disabled={!connected}
        gradient={{
          from: 'rgba(122, 39, 39, 1)',
          to: 'rgba(191, 10, 10, 1)',
          deg: 91,
        }}
      >
        Sync to Cloud
      </Button>

      <Modal
        opened={opened}
        onClose={close}
        title="Sync your changes"
        size={diffMode ? '90%' : '600px'}
      >
        <div>
          <Tabs defaultValue="local">
            <Tabs.List>
              <Tabs.Tab value="local">Local</Tabs.Tab>
              <Tabs.Tab value="github" leftSection={<IconBrandGithub width={15} height={15} />}>
                GitHub Actions
              </Tabs.Tab>
              <Tabs.Tab value="ci">Other CI</Tabs.Tab>
            </Tabs.List>

            <Tabs.Panel value="local" pt={15}>
              <LocalTab
                onDiff={() => {
                  setDiffMode(true);
                }}
                onDeploy={() => {
                  close();
                }}
              />
            </Tabs.Panel>

            <Tabs.Panel value="github" pt={15}>
              We provide a GitHub Action that will sync your state to Novu Cloud. <br /> <br />
              <Code block>
                <pre>
                  {`name: Deploy Echo State to Novu

# workflow triggers
name: Deploy to Novu
on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Sync State to Novu
        uses: novuhq/actions-echo-sync@v0.0.4
        with:
          novu-api-key: \${{secrets.NOVU_API_KEY}}
          echo-url: "YOUR_ECHO_URL"
`}
                </pre>
              </Code>
            </Tabs.Panel>

            <Tabs.Panel value="ci" pt={15}>
              Please use our CLI Utility to integrate with your CI of choice.
              <Code block mt={10}>
                {'npx novu-labs@latest sync --echo-url <YOUR URL> --api-key <NOVU_API_KEY>'}
              </Code>
            </Tabs.Panel>
          </Tabs>
        </div>
      </Modal>
    </>
  );
}

function LocalTab({ onDeploy, onDiff }) {
  const { track, getAnonymousId } = useSegment();

  const [missingApiKeyState, setMissingApiKeyState] = useState(false);
  const [loadingSync, setLoadingSync] = useState(false);
  const [diffState, setDiffState] = useState<{ current?: string; new?: string } | null>(null);

  const [remoteEchoUrl, setRemoteEchoUrl] = useLocalStorage({
    key: 'remote_echo_url',
    defaultValue: '',
  });
  const [echoUrlFormState, setEchoUrlFormState] = useState<string>();

  useEffect(() => {
    setEchoUrlFormState(remoteEchoUrl);
  }, [remoteEchoUrl]);

  const [loadingDeploy, setLoadingDeploy] = useState(false);

  const [localEchoUrl] = useLocalStorage({
    key: 'echo_url',
    defaultValue: '',
  });

  async function diffChanges() {
    setLoadingSync(true);
    track('Diff Change');

    try {
      const anonymousId = await getAnonymousId();
      const result = await fetch('/api/state/diff', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...getEchoHeaders(),
          'x-novu-anonymous': anonymousId,
        },
        body: JSON.stringify({ echoUrl: echoUrlFormState }),
      }).then(async (response) => {
        if (response.ok) {
          setMissingApiKeyState(false);

          return response.json();
        }

        const jsonError = await response.json();

        if (jsonError.message?.includes('API Key is missing')) {
          track('API Key Is Missing on Sync');

          setMissingApiKeyState(true);

          notifications.show({
            color: 'red',
            title: 'API Key is Missing',
            message: jsonError.message || 'Something bad happened',
          });
        } else {
          setMissingApiKeyState(false);

          notifications.show({
            color: 'red',
            title: 'Oh oh... Something went wrong',
            message: jsonError.message || 'Something bad happened',
          });
        }

        throw new Error(jsonError.message);
      });

      setDiffState({
        current: JSON.stringify(result.data?.current, null, 2),
        new: JSON.stringify(result.data?.new, null, 2),
      });

      onDiff();
    } catch (e: any) {
      console.error(e.message);
    }
    setLoadingSync(false);
  }

  function saveUrl() {
    if (!echoUrlFormState) return null;

    if (echoUrlFormState?.includes('localhost')) {
      const port = echoUrlFormState.match(/:(\d+)/) || ['', '<PORT>'];

      const openModal = () =>
        modals.openConfirmModal({
          title: 'localhost is not allowed for sync',
          children: (
            <div>
              <Text size="sm">
                Novu Cloud Will not be able to communicate with your {echoUrlFormState} endpoint, as
                its running on your local machine.
                <br /> Please use <Code>npx localtunnel --port {port[1]}</Code> to expose your local
                server to the internet.
              </Text>
              <br />
              <Text>For production environments we recommend syncing state using CI.</Text>
            </div>
          ),
          labels: { confirm: 'Ok', cancel: 'Cancel' },
          onCancel: () => console.log('Cancel'),
          onConfirm: () => console.log('Confirmed'),
        });

      openModal();
      return;
    }
    setRemoteEchoUrl(echoUrlFormState);

    setLoadingSync(true);

    diffChanges();
  }

  async function deployState() {
    if (!echoUrlFormState) return null;

    setLoadingDeploy(true);

    setRemoteEchoUrl(echoUrlFormState);
    track('Deploy State');

    try {
      const anonymousId = await getAnonymousId();

      const result = await fetch('/api/state/sync', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...getEchoHeaders(),
          'x-novu-anonymous': anonymousId,
        },
        body: JSON.stringify({
          echoUrl: echoUrlFormState,
        }),
      }).then(async (response) => {
        if (response.ok) {
          return response.json();
        }

        track('Error Deploy State');

        const jsonError = await response.json();

        notifications.show({
          color: 'red',
          title: 'Oh oh... Something went wrong',
          message: jsonError.message || 'Something bad happened',
        });

        throw new Error(jsonError.message);
      });

      onDeploy();
      notifications.show({
        color: 'green',
        title: 'Sync Complete',
        message: (
          <>
            We have successfully synced your state to Novu Cloud.{' '}
            <a href="https://web.novu.co/workflows?source=studio-sync" target={'_blank'}>
              Test your workflows
            </a>{' '}
            on Novu Cloud
          </>
        ),
        autoClose: false,
      });
    } catch (e: any) {
      console.error(e.message);
    }

    setLoadingDeploy(false);
  }

  async function changeUrl(url: string) {
    setEchoUrlFormState(url);
  }
  const port = localEchoUrl?.match(/:(\d+)/) || ['', '<PORT>'];
  const isIdentical = useMemo(
    () => JSON.stringify(diffState?.current) === JSON.stringify(diffState?.new),
    [diffState]
  );

  return (
    <Box
      style={{
        maxWidth: '100%',
        position: 'relative',
        minHeight: 300,
        display: 'flex',
        justifyContent: 'space-between',
        flexDirection: 'column',
      }}
    >
      {missingApiKeyState ? (
        <Box>
          To Perform this action you need to obtain the Novu Cloud API Key. Which located in the{' '}
          <a style={{ color: 'inherit' }} href={'https://web.novu.co/settings'} target={'_blank'}>
            Settings Page
          </a>
          .
          <Box style={{ display: 'flex', justifyContent: 'center' }} mt={15}>
            <Button
              onClick={() => {
                track('Have an Account Sync Flow');

                window.open('https://web.novu.co/settings?utm_campaign=echo_studio', '_blank');
              }}
              mr={10}
              variant="default"
              size="xs"
              gradient={{
                from: 'rgba(122, 39, 39, 1)',
                to: 'rgba(191, 10, 10, 1)',
                deg: 91,
              }}
            >
              I Already Have an Account
            </Button>
            <Button
              onClick={() => {
                track('Create new account Sync Flow');

                window.open('https://web.novu.co/settings?utm_campaign=echo_studio', '_blank');
              }}
              variant="default"
              size="xs"
              gradient={{
                from: 'rgba(122, 39, 39, 1)',
                to: 'rgba(191, 10, 10, 1)',
                deg: 91,
              }}
              color={'black'}
            >
              Create New Account
            </Button>
          </Box>
          <Divider mt={20} mb={10} />
          After obtaining the API Key for your environment, add it to the Echo Client
          <CodeHighlightTabs
            style={{ marginTop: 15 }}
            code={[
              {
                fileName: 'app/echo/client.ts',
                code: `
import { Echo } from '@novu/echo';

export const echo = new Echo({
  /**
  * This will be needed when you want to sync your work.
  * Find your API-Key here: https://web.novu.co/settings
  */
  apiKey: process.env.NOVU_API_KEY,
  
  /**
  * Enable this flag only during local development
  */
  devModeBypassAuthentication: process.env.NODE_ENV === 'development'
});

                `,
                language: 'typescript',
              },
            ]}
          />
        </Box>
      ) : (
        <DiffViewContainer
          diffState={diffState}
          loadingSync={loadingSync}
          port={port}
          isIdentical={isIdentical}
        />
      )}

      <div
        style={{
          position: 'sticky',
          bottom: 0,
          left: 0,
          right: 0,
          zIndex: 1,
          background: 'var(--mantine-color-body)',
          padding: '15px 0',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
        }}
      >
        <Divider mt={15} />

        <Input.Wrapper
          label="Echo Endpoint"
          withAsterisk
          description="Full URL including https and the echo path"
          style={{ width: '100%' }}
        >
          <Input
            onChange={(e) => changeUrl(e.target.value)}
            value={echoUrlFormState}
            placeholder="Your Cloud Echo Endpoint"
          />
        </Input.Wrapper>

        <div
          style={{
            display: 'flex',
            alignItems: 'flex-end',
            alignSelf: 'flex-end',
            paddingLeft: 20,
          }}
        >
          {diffState ? (
            <Button
              loading={loadingDeploy}
              onClick={deployState}
              leftSection={<IconRefresh size={14} />}
              variant="default"
              size="sm"
              gradient={{
                from: 'rgba(122, 39, 39, 1)',
                to: 'rgba(191, 10, 10, 1)',
                deg: 91,
              }}
            >
              Deploy Changes
            </Button>
          ) : (
            <Button
              loading={loadingSync}
              onClick={saveUrl}
              variant="default"
              size="sm"
              gradient={{
                from: 'rgba(122, 39, 39, 1)',
                to: 'rgba(191, 10, 10, 1)',
                deg: 91,
              }}
            >
              {echoUrlFormState ? 'Create Diff' : 'Save'}
            </Button>
          )}
        </div>
      </div>
    </Box>
  );
}

function DiffViewContainer({ diffState, loadingSync, port, isIdentical }) {
  return (
    <>
      {' '}
      {!diffState && !loadingSync ? (
        <div>
          For local experimentation purposes you will need to create a localtunnel that our cloud
          environment can reach. <br /> <br />
          <Code block>npx localtunnel --port {port[1]}</Code> <br />
          For production environments we recommend syncing state using CI.
        </div>
      ) : (
        <DiffView diffState={diffState} isIdentical={isIdentical} />
      )}{' '}
    </>
  );
}

function DiffView({ isIdentical, diffState }) {
  return isIdentical ? (
    <Container>
      <div style={{ width: '100%', marginTop: 30, marginBottom: 30 }}>
        <Alert variant="outline" color="grey" title="Identical State" icon={<IconInfoCircle />}>
          <div style={{ color: '#cdcdcd' }}>
            No Changes were detected between your local and remote state.
          </div>
        </Alert>
      </div>
    </Container>
  ) : (
    <ReactDiffViewer
      styles={{
        content: {
          width: '100%',
        },
      }}
      leftTitle="Cloud State"
      rightTitle="Local State"
      oldValue={diffState?.current}
      newValue={diffState?.new}
      useDarkTheme
      compareMethod={DiffMethod.WORDS}
    />
  );
}
