import { joinClassNames, sleep } from '@decky/ui';
import { FunctionComponent, useEffect, useReducer, useState } from 'react';

import { disablePlugin, uninstallPlugin } from '../plugin';
import { VerInfo, doRestart, doShutdown } from '../updater';
import { ValveReactErrorInfo, getLikelyErrorSourceFromValveReactError } from '../utils/errors';
import { useSetting } from '../utils/hooks/useSetting';
import { UpdateBranch } from './settings/pages/general/BranchSelect';

interface DeckyErrorBoundaryProps {
  error: ValveReactErrorInfo;
  errorKey: string;
  identifier: string;
  reset: () => void;
}

declare global {
  interface Window {
    SystemNetworkStore?: any;
  }
}

const classes = {
  root: 'deckyErrorBoundary',
  likelyOccurred: 'likely-occured-msg',
  panel: 'panel-section',
  panelHeader: 'panel-header',
  trace: 'trace',
  rowList: 'row-list',
  rowItem: 'row-item',
  buttonDescRow: 'button-description-row',
  flexRowWGap: 'flex-row',
  marginBottom: 'margin-bottom',
  swipePrompt: 'swipe-prompt',
};

const vars = {
  scrollBarwidth: '18px',
  rootMarginLeft: '15px',
  panelXPadding: '20px',
};

export const startSSH = DeckyBackend.callable('utilities/start_ssh');
export const starrCEFForwarding = DeckyBackend.callable('utilities/allow_remote_debugging');

function ipToString(ip: number) {
  return [(ip >>> 24) & 255, (ip >>> 16) & 255, (ip >>> 8) & 255, (ip >>> 0) & 255].join('.');
}

// Intentionally not localized since we can't really trust React here
const DeckyErrorBoundary: FunctionComponent<DeckyErrorBoundaryProps> = ({ error, identifier, reset }) => {
  const [actionLog, addLogLine] = useReducer((log: string, line: string) => (log += '\n' + line), '');
  const [actionsEnabled, setActionsEnabled] = useState<boolean>(true);
  const [debugAllowed, setDebugAllowed] = useState<boolean>(true);
  // Intentionally doesn't use DeckyState.
  const [versionInfo, setVersionInfo] = useState<VerInfo>();
  const [errorSource, wasCausedByPlugin, shouldReportToValve] = getLikelyErrorSourceFromValveReactError(error);
  useEffect(() => {
    if (!shouldReportToValve) DeckyPluginLoader.errorBoundaryHook.temporarilyDisableReporting();
    DeckyPluginLoader.updateVersion().then(setVersionInfo);
  }, []);

  const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Stable);
  const [isChecking, setIsChecking] = useState<boolean>(false);
  const [updateProgress, setUpdateProgress] = useState<number>(-1);
  const [versionToUpdateTo, setSetVersionToUpdateTo] = useState<string>('');

  useEffect(() => {
    const a = DeckyBackend.addEventListener('updater/update_download_percentage', (percentage) => {
      setUpdateProgress(percentage);
    });

    const b = DeckyBackend.addEventListener('updater/finish_download', () => {
      setUpdateProgress(-2);
    });

    return () => {
      DeckyBackend.removeEventListener('updater/update_download_percentage', a);
      DeckyBackend.removeEventListener('updater/finish_download', b);
    };
  }, []);

  return (
    <>
      <style>
        {`
          *:has(> .${classes.root}) {
            margin-top: var(--basicui-header-height);
            overflow: scroll !important;
            background: #000;
          }
          *:has(> .${classes.root})::-webkit-scrollbar {
            display: initial !important;
            width: ${vars.scrollBarwidth};
            height: 0px;
          }
          *:has(> .${classes.root})::-webkit-scrollbar-thumb {
            background: #4349535e;
          }
          .${classes.root} {
            color: #93929e;
            font-size: 15px;
            margin: 10px 0px 40px ${vars.rootMarginLeft};
            overflow: visible;
          }
          .${classes.root} button,
          .${classes.root} select {
            border: none;
            padding: 4px 16px !important;
            background: #333;
            color: #ddd;
            font-size: 12px;
            border-radius: 3px;
            outline: none;
            height: 28px;
          }
          .${classes.panel} {
            background: #080808;
            padding: 8px ${vars.panelXPadding};
            border-radius: 3px;
            /* box-shadow: 9px 9px 20px -5px rgb(0 0 0 / 89%); */
          }
          .${classes.panelHeader} {
            font-size: 18px;
            font-weight: bolder;
            text-transform: uppercase;
          }
          .${classes.likelyOccurred} {
            font-size: 22px;
            font-weight: bold;
            color: #588fb4;
          }
          .${classes.rowItem} {
            position: relative;
          }
          .${classes.rowItem}:not(:last-child)::after {
            content: '';
            position: absolute;
            bottom: -4.5px;
            left: 5px;
            right: 15px;
            height: 0.5px;
            background: #3c3c3c47;
          }
          .${classes.flexRowWGap},
          .${classes.buttonDescRow},
          .${classes.rowList},
          .${classes.panel} {
            display: flex;
          }

          .${classes.rowList},
          .${classes.panel} {
            flex-direction: column;
          }
          .${classes.flexRowWGap},
          .${classes.rowList} {
            gap: 8px;
          }
          .${classes.marginBottom} {
            margin-bottom: 10px;
          }
          .${classes.buttonDescRow} {
            justify-content: space-between;
            align-items: center;
          }

          .${classes.swipePrompt} {
            display: flex;
            align-items: center;
            text-align: center;
            position: relative;
            font-style: italic;
            font-size: small;
            margin: 16px 0;
          }
          .${classes.swipePrompt} span {
            padding: 0 8px;
            background-color: #000;
            position: relative;
            z-index: 1;
          }
          .${classes.swipePrompt}::before,
          .${classes.swipePrompt}::after {
            content: "";
            flex-grow: 1;
            border-bottom: 1px solid #474752;
            top: 50%;
          }
          .${classes.swipePrompt}::before {
            right: 50%;
            margin-right: 8px;
          }
          .${classes.swipePrompt}::after {
            left: 50%;
            margin-left: 8px;
          }
        `}
      </style>
      <div className={classes.root}>
        <div className={classes.marginBottom}>An error occurred while rendering this content.</div>
        <pre className={joinClassNames(classes.marginBottom)} style={{ marginTop: '0px' }}>
          <code>
            {identifier && `Error Reference: ${identifier}`}
            {versionInfo?.current && `\nDecky Version: ${versionInfo.current}`}
          </code>
        </pre>
        <div className={joinClassNames(classes.likelyOccurred, classes.marginBottom)}>
          This error likely occurred in {errorSource}.
        </div>
        {actionLog?.length > 0 && (
          <pre>
            <code>
              Running actions...
              {actionLog}
            </code>
          </pre>
        )}
        {actionsEnabled && (
          <div className={classes.panel}>
            <div className={classes.flexRowWGap} style={{ alignItems: 'center', marginBottom: '8px' }}>
              <div className={classes.panelHeader}>Actions</div>
              <div style={{ fontSize: 'small', fontStyle: 'italic' }}>
                Use the touch screen. Solutions are listed in the recommended order. If you are still experiencing
                issues, please post in the #loader-support channel at decky.xyz/discord.
              </div>
            </div>
            <div className={classes.rowList}>
              <div className={joinClassNames(classes.rowItem, classes.buttonDescRow)}>
                Retry the action or restart
                <div className={classes.flexRowWGap}>
                  <button onClick={reset}>Retry</button>
                  <button
                    onClick={() => {
                      addLogLine('Restarting Steam...');
                      SteamClient.User.StartRestart(false);
                    }}
                  >
                    Restart Steam
                  </button>
                  <button
                    onClick={async () => {
                      setActionsEnabled(false);
                      addLogLine('Restarting Decky...');
                      doRestart();
                      await sleep(2000);
                      addLogLine('Reloading UI...');
                    }}
                  >
                    Restart Decky
                  </button>
                </div>
              </div>
              {wasCausedByPlugin && (
                <div className={joinClassNames(classes.rowItem, classes.buttonDescRow)}>
                  Disable or uninstall the suspected plugin
                  <div className={classes.flexRowWGap}>
                    <button
                      onClick={async () => {
                        setActionsEnabled(false);
                        addLogLine(`Disabling ${errorSource}...`);
                        await disablePlugin(errorSource);
                        await sleep(1000);
                        addLogLine('Restarting Decky...');
                        doRestart();
                        await sleep(2000);
                        addLogLine('Restarting Steam...');
                        await sleep(500);
                        SteamClient.User.StartRestart(false);
                      }}
                    >
                      Disable {errorSource}
                    </button>
                    <button
                      onClick={async () => {
                        setActionsEnabled(false);
                        addLogLine(`Uninstalling ${errorSource}...`);
                        await uninstallPlugin(errorSource);
                        await DeckyPluginLoader.frozenPluginsService.invalidate();
                        await DeckyPluginLoader.hiddenPluginsService.invalidate();
                        await sleep(1000);
                        addLogLine('Restarting Decky...');
                        doRestart();
                        await sleep(2000);
                        addLogLine('Restarting Steam...');
                        await sleep(500);
                        SteamClient.User.StartRestart(false);
                      }}
                    >
                      Uninstall {errorSource}
                    </button>
                  </div>
                </div>
              )}
              <div className={joinClassNames(classes.rowItem, classes.buttonDescRow)}>
                Disable all plugins
                <button
                  onClick={async () => {
                    setActionsEnabled(false);
                    addLogLine(`Disabling plugins...`);
                    await DeckyBackend.call('utilities/set_all_plugins_disabled');
                    await sleep(1000);
                    addLogLine('Restarting Decky...');
                    doRestart();
                    await sleep(2000);
                    addLogLine('Restarting Steam...');
                    await sleep(500);
                    SteamClient.User.StartRestart(false);
                  }}
                >
                  Disable All Plugins
                </button>
              </div>
              {
                <div className={joinClassNames(classes.rowItem, classes.buttonDescRow)}>
                  {updateProgress > -1
                    ? 'Update in progress... ' + updateProgress + '%'
                    : updateProgress == -2
                      ? 'Update complete. Restarting...'
                      : 'Check for Decky updates'}
                  {
                    <div className={classes.flexRowWGap}>
                      {updateProgress == -1 && (
                        <>
                          <select
                            onChange={async (e) => {
                              const branch = parseInt(e.target.value);
                              setSelectedBranch(branch);
                              setSetVersionToUpdateTo('');
                            }}
                          >
                            <option value="0" selected={selectedBranch == UpdateBranch.Stable}>
                              Stable
                            </option>
                            <option value="1" selected={selectedBranch == UpdateBranch.Prerelease}>
                              Pre-Release
                            </option>
                            <option value="2" selected={selectedBranch == UpdateBranch.Testing}>
                              Testing
                            </option>
                          </select>
                          <button
                            disabled={updateProgress != -1 || isChecking}
                            onClick={async () => {
                              if (versionToUpdateTo == '') {
                                setIsChecking(true);
                                const versionInfo = (await DeckyBackend.callable(
                                  'updater/check_for_updates',
                                )()) as unknown as VerInfo;
                                setIsChecking(false);
                                if (versionInfo?.remote && versionInfo?.remote?.tag_name != versionInfo?.current) {
                                  setSetVersionToUpdateTo(versionInfo.remote.tag_name);
                                } else {
                                  setSetVersionToUpdateTo('');
                                }
                              } else {
                                DeckyBackend.callable('updater/do_update')();
                                setUpdateProgress(0);
                              }
                            }}
                          >
                            {' '}
                            {isChecking
                              ? 'Checking for updates...'
                              : versionToUpdateTo != ''
                                ? 'Update to ' + versionToUpdateTo
                                : 'Check for updates'}
                          </button>
                        </>
                      )}
                    </div>
                  }
                </div>
              }
              <div className={joinClassNames(classes.rowItem, classes.buttonDescRow)}>
                Disable Decky until next boot
                <button
                  onClick={async () => {
                    setActionsEnabled(false);
                    addLogLine('Stopping Decky...');
                    doShutdown();
                    await sleep(5000);
                    addLogLine('Restarting Steam...');
                    SteamClient.User.StartRestart(false);
                  }}
                >
                  Disable Decky
                </button>
              </div>
              {debugAllowed && (
                <div className={joinClassNames(classes.rowItem, classes.buttonDescRow)}>
                  Enable remote debugging and SSH until next boot (for developers)
                  <button
                    onClick={async () => {
                      setDebugAllowed(false);
                      addLogLine('Enabling CEF debugger forwarding...');
                      await starrCEFForwarding();
                      addLogLine('Enabling SSH...');
                      await startSSH();
                      addLogLine('Ready for debugging!');
                      if (window?.SystemNetworkStore?.wirelessNetworkDevice?.ip4?.addresses?.[0]?.ip) {
                        const ip = ipToString(window.SystemNetworkStore.wirelessNetworkDevice.ip4.addresses[0].ip);
                        addLogLine(`CEF Debugger: http://${ip}:8081`);
                        addLogLine(`SSH: deck@${ip}`);
                      }
                    }}
                  >
                    Enable
                  </button>
                </div>
              )}
            </div>
          </div>
        )}
        {actionsEnabled && (
          <div className={classes.swipePrompt}>
            <span>Swipe to scroll</span>
          </div>
        )}
        <div className={classes.panel}>
          <div className={classes.panelHeader}>Trace</div>
          <pre
            style={{
              margin: `8px calc(-1 * ${vars.panelXPadding})`,
              userSelect: 'auto',
              overflowX: 'scroll',
              padding: `0px ${vars.panelXPadding}`,
              maskImage: `linear-gradient(to right, transparent, black ${vars.panelXPadding}, black calc(100% - ${vars.panelXPadding}), transparent)`,
            }}
          >
            <code>
              {error.error.stack}
              {'\n\n'}
              Component Stack:
              {error.info.componentStack}
            </code>
          </pre>
        </div>
      </div>
    </>
  );
};

export default DeckyErrorBoundary;
