import { isBoolean } from 'lodash';
import useUpdate from 'react-use/lib/useUpdate';
import useAsync from 'react-use/lib/useAsync';
import { shell } from 'electron';
import useBoolean from 'react-use/lib/useBoolean';
import type { SessionFeatureFlagsKeys } from '../../../window';
import { Flex } from '../../basic/Flex';
import { SessionToggle } from '../../basic/SessionToggle';
import { HintText, SpacerXS } from '../../basic/Text';
import { localize } from '../../../localization/localeTools';
import { CopyToClipboardIcon } from '../../buttons';
import { saveLogToDesktop } from '../../../util/logging';
import { Localizer } from '../../basic/Localizer';
import { SessionButton } from '../../basic/SessionButton';
import { ToastUtils, UserUtils } from '../../../session/utils';
import { getLatestReleaseFromFileServer } from '../../../session/apis/file_server_api/FileServerApi';
import { SessionSpinner } from '../../loading';

export const DebugActions = () => {
  const [loadingLatestRelease, setLoadingLatestRelease] = useBoolean(false);

  return (
    <>
      <h2>Actions</h2>
      <SpacerXS />
      <Flex
        container={true}
        width="100%"
        justifyContent="flex-start"
        alignItems="flex-start"
        flexWrap="wrap"
        flexGap="var(--margins-md) var(--margins-lg)"
      >
        <SessionButton
          onClick={() => {
            void saveLogToDesktop();
          }}
        >
          <Localizer token="helpReportABugExportLogs" />
        </SessionButton>

        {window.getCommitHash() ? (
          <SessionButton
            onClick={() => {
              void shell.openExternal(
                `https://github.com/session-foundation/session-desktop/commit/${window.getCommitHash()}`
              );
            }}
          >
            Go to commit
          </SessionButton>
        ) : null}

        <SessionButton
          onClick={() => {
            void shell.openExternal(
              `https://github.com/session-foundation/session-desktop/releases/tag/v${window.getVersion()}`
            );
          }}
        >
          <Localizer token="updateReleaseNotes" />
        </SessionButton>

        <SessionButton
          onClick={async () => {
            const userEd25519SecretKey = (await UserUtils.getUserED25519KeyPairBytes())
              ?.privKeyBytes;
            if (!userEd25519SecretKey) {
              window.log.error('[debugMenu] no userEd25519SecretKey');
              return;
            }
            setLoadingLatestRelease(true);
            const versionNumber = await getLatestReleaseFromFileServer(userEd25519SecretKey);
            setLoadingLatestRelease(false);

            if (versionNumber) {
              ToastUtils.pushToastInfo('debugLatestRelease', `v${versionNumber}`);
            } else {
              ToastUtils.pushToastError('debugLatestRelease', 'Failed to fetch latest release');
            }
          }}
        >
          <SessionSpinner loading={loadingLatestRelease} color={'var(--text-primary-color)'} />
          {!loadingLatestRelease ? 'Check latest release' : null}
        </SessionButton>
      </Flex>
    </>
  );
};

const unsupportedFlags = ['useTestNet'];
const untestedFlags = ['useOnionRequests', 'useClosedGroupV3', 'replaceLocalizedStringsWithKeys'];

const handleFeatureFlagToggle = async (
  forceUpdate: () => void,
  flag: SessionFeatureFlagsKeys,
  parentFlag?: SessionFeatureFlagsKeys
) => {
  const currentValue = parentFlag
    ? (window as any).sessionFeatureFlags[parentFlag][flag]
    : (window as any).sessionFeatureFlags[flag];

  if (parentFlag) {
    (window as any).sessionFeatureFlags[parentFlag][flag] = !currentValue;
    window.log.debug(`[debugMenu] toggled ${parentFlag}.${flag} to ${!currentValue}`);
  } else {
    (window as any).sessionFeatureFlags[flag] = !currentValue;
    window.log.debug(`[debugMenu] toggled ${flag} to ${!currentValue}`);
  }

  forceUpdate();
};

const FlagToggle = ({
  forceUpdate,
  flag,
  value,
  parentFlag,
}: {
  forceUpdate: () => void;
  flag: SessionFeatureFlagsKeys;
  value: any;
  parentFlag?: SessionFeatureFlagsKeys;
}) => {
  const key = `feature-flag-toggle${parentFlag ? `-${parentFlag}` : ''}-${flag}`;
  return (
    <Flex
      key={key}
      id={key}
      container={true}
      width="100%"
      alignItems="center"
      justifyContent="space-between"
    >
      <span>
        {flag}
        {untestedFlags.includes(flag) ? <HintText>Untested</HintText> : null}
      </span>
      <SessionToggle
        active={value}
        onClick={() => void handleFeatureFlagToggle(forceUpdate, flag, parentFlag)}
      />
    </Flex>
  );
};

export const FeatureFlags = ({ flags }: { flags: Record<string, any> }) => {
  const forceUpdate = useUpdate();
  return (
    <Flex
      container={true}
      width={'100%'}
      flexDirection="column"
      justifyContent="flex-start"
      alignItems="flex-start"
      flexGap="var(--margins-xs)"
    >
      <Flex container={true} alignItems="center">
        <h2>Feature Flags</h2>
        <HintText>Experimental</HintText>
      </Flex>
      <i>
        Changes are temporary. You can clear them by reloading the window or restarting the app.
      </i>
      <SpacerXS />
      {Object.entries(flags).map(([key, value]) => {
        const flag = key as SessionFeatureFlagsKeys;
        if (unsupportedFlags.includes(flag)) {
          return null;
        }

        if (!isBoolean(value)) {
          return (
            <>
              <h3>{flag}</h3>
              {Object.entries(value).map(([k, v]: [string, any]) => {
                const nestedFlag = k as SessionFeatureFlagsKeys;
                return (
                  <FlagToggle
                    forceUpdate={forceUpdate}
                    flag={nestedFlag}
                    value={v}
                    parentFlag={flag}
                  />
                );
              })}
            </>
          );
        }
        return <FlagToggle forceUpdate={forceUpdate} flag={flag} value={value} />;
      })}
    </Flex>
  );
};

export const AboutInfo = () => {
  const environmentStates = [];

  if (window.getEnvironment() !== 'production') {
    environmentStates.push(window.getEnvironment());
  }

  if (window.getAppInstance()) {
    environmentStates.push(window.getAppInstance());
  }

  const aboutInfo = [
    `${localize('updateVersion').withArgs({ version: window.getVersion() })}`,
    `${localize('systemInformationDesktop').withArgs({ information: window.getOSRelease() })}`,
    `${localize('commitHashDesktop').withArgs({ hash: window.getCommitHash() || window.i18n('unknown') })}`,
    `${environmentStates.join(' - ')}`,
  ];

  return (
    <Flex
      container={true}
      width={'100%'}
      flexDirection="column"
      justifyContent="flex-start"
      alignItems="flex-start"
      flexWrap="wrap"
    >
      <SpacerXS />
      <Flex container={true} width="100%" alignItems="center" flexGap="var(--margins-xs)">
        <h2>About</h2>
        <CopyToClipboardIcon iconSize={'medium'} copyContent={aboutInfo.join('\n')} />
      </Flex>
      <Flex
        container={true}
        width="100%"
        flexDirection="column"
        justifyContent="space-between"
        alignItems="center"
        flexGap="var(--margins-xs)"
      >
        {aboutInfo.map((info, index) => (
          <Flex
            key={`debug-about-info-${index}`}
            container={true}
            width="100%"
            alignItems="flex-start"
            flexGap="var(--margins-xs)"
          >
            <p style={{ userSelect: 'text', lineHeight: 1.5 }}>{info}</p>
            <CopyToClipboardIcon iconSize={'medium'} copyContent={info} />
          </Flex>
        ))}
        <SpacerXS />
      </Flex>
    </Flex>
  );
};

export const OtherInfo = () => {
  const otherInfo = useAsync(async () => {
    const { id, vbid } = await window.getUserKeys();
    return [`${localize('accountIdYours')}: ${id}`, `VBID: ${vbid}`];
  }, []);

  return (
    <Flex
      container={true}
      width={'100%'}
      flexDirection="column"
      justifyContent="flex-start"
      alignItems="flex-start"
      flexWrap="wrap"
    >
      <SpacerXS />
      <Flex container={true} width="100%" alignItems="center" flexGap="var(--margins-xs)">
        <h2>Other Info</h2>
        {otherInfo.value ? (
          <CopyToClipboardIcon iconSize={'medium'} copyContent={otherInfo.value.join('\n')} />
        ) : null}
      </Flex>
      <Flex
        container={true}
        width="100%"
        flexDirection="column"
        justifyContent="space-between"
        alignItems="center"
        flexGap="var(--margins-xs)"
      >
        {otherInfo.loading ? (
          <p>{localize('loading')}</p>
        ) : otherInfo.error ? (
          <p style={{ color: 'var(--danger-color)', userSelect: 'text' }}>
            {localize('theError')}: {otherInfo.error.message || localize('errorUnknown')}
          </p>
        ) : null}
        {otherInfo.value
          ? otherInfo.value.map((info, index) => (
              <Flex
                key={`debug-other-info-${index}`}
                container={true}
                width="100%"
                alignItems="flex-start"
                flexGap="var(--margins-xs)"
              >
                <p style={{ userSelect: 'text', lineHeight: 1.5 }}>{info}</p>
                <CopyToClipboardIcon iconSize={'medium'} copyContent={info} />
              </Flex>
            ))
          : null}
      </Flex>
    </Flex>
  );
};