import React, { useState, useEffect, useRef } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { css } from 'glamor';
import { Route, Switch, Redirect } from 'react-router-dom';
import * as actions from 'loot-core/src/client/actions';
import {
  View,
  Text,
  Button,
  ButtonLink,
  ButtonWithLoading,
  AnchorLink,
  Link,
  Input
} from 'loot-design/src/components/common';
import { send, listen } from 'loot-core/src/platform/client/fetch';
import { numberFormats } from 'loot-core/src/shared/util';
import { styles, colors } from 'loot-design/src/style';
import { Information, Warning, Error } from 'loot-design/src/components/alerts';
import Checkmark from 'loot-design/src/svg/v1/Checkmark';
import CheveronDown from 'loot-design/src/svg/v1/CheveronDown';
import ExpandArrow from 'loot-design/src/svg/ExpandArrow';
import ExclamationSolid from 'loot-design/src/svg/v1/ExclamationSolid';
import Platform from 'loot-core/src/client/platform';

import useServerVersion from '../hooks/useServerVersion';

let dateFormats = [
  { value: 'MM/dd/yyyy', label: 'MM/DD/YYYY' },
  { value: 'dd/MM/yyyy', label: 'DD/MM/YYYY' },
  { value: 'yyyy-MM-dd', label: 'YYYY-MM-DD' },
  { value: 'MM.dd.yyyy', label: 'MM.DD.YYYY' },
  { value: 'dd.MM.yyyy', label: 'DD.MM.YYYY' }
];

function Title({ name, style }) {
  return (
    <View
      style={[
        { fontSize: 20, fontWeight: 500, marginBottom: 20, flexShrink: 0 },
        style
      ]}
    >
      {name}
    </View>
  );
}

function Advanced({ prefs, userData, pushModal, resetSync }) {
  let [expanded, setExpanded] = useState(true);
  let [resetting, setResetting] = useState(false);
  let [resettingCache, setResettingCache] = useState(false);

  async function onResetSync() {
    setResetting(true);
    await resetSync();
    setResetting(false);
  }

  async function onResetCache() {
    setResettingCache(true);
    await send('reset-budget-cache');
    setResettingCache(false);
  }

  return (
    <View style={{ alignItems: 'flex-start', marginTop: 55 }}>
      <View
        style={[
          {
            fontSize: 15,
            marginBottom: 20,
            flexDirection: 'row',
            alignItems: 'center'
          },
          styles.staticText
        ]}
        onClick={() => setExpanded(!expanded)}
      >
        <ExpandArrow
          width={8}
          height={8}
          style={{
            marginRight: 5,
            transition: 'transform .2s',
            transform: !expanded && 'rotateZ(-90deg)'
          }}
        />
        Advanced
      </View>

      {expanded && (
        <View style={{ marginBottom: 20, alignItems: 'flex-start' }}>
          <Text>
            <strong>Budget ID</strong>: {prefs.id}
          </Text>

          <View
            style={{
              backgroundColor: colors.n9,
              alignItems: 'flex-start',
              padding: 15,
              borderRadius: 4,
              marginTop: 20,
              border: '1px solid ' + colors.n8
            }}
          >
            <Text style={{ marginBottom: 10, width: 500, lineHeight: 1.5 }}>
              <strong>Reset budget cache</strong> will clear all cached values
              for the budget and recalculate the entire budget. All values in
              the budget are cached for performance reasons, and if there is a
              bug in the cache you won't see correct values. There is no danger
              in resetting the cache. Hopefully you never have to do this.
            </Text>
            <ButtonWithLoading loading={resettingCache} onClick={onResetCache}>
              Reset budget cache
            </ButtonWithLoading>
          </View>

          <View
            style={{
              backgroundColor: colors.n9,
              alignItems: 'flex-start',
              padding: 15,
              borderRadius: 4,
              marginTop: 20,
              border: '1px solid ' + colors.n8
            }}
          >
            <Text style={{ marginBottom: 10, width: 500, lineHeight: 1.5 }}>
              <strong>Reset sync</strong> will remove all local data used to
              track changes for syncing, and create a fresh sync id on our
              server. This file on other devices will have to be re-downloaded
              to use the new sync id. Use this if there is a problem with
              syncing and you want to start fresh.
            </Text>

            <ButtonWithLoading loading={resetting} onClick={onResetSync}>
              Reset sync
            </ButtonWithLoading>
            <Text style={{ marginTop: 15, color: colors.n4, fontSize: 12 }}>
              Sync ID: {prefs.groupId || '(none)'}
            </Text>
          </View>
        </View>
      )}
    </View>
  );
}

function GlobalSettings({
  globalPrefs,
  userData,
  saveGlobalPrefs,
  pushModal,
  closeBudget
}) {
  let [documentDirChanged, setDirChanged] = useState(false);
  let dirScrolled = useRef(null);

  useEffect(() => {
    if (dirScrolled.current) {
      dirScrolled.current.scrollTo(10000, 0);
    }
  }, []);

  async function onChooseDocumentDir() {
    let res = await window.Actual.openFileDialog({
      properties: ['openDirectory']
    });
    if (res) {
      saveGlobalPrefs({ documentDir: res[0] });
      setDirChanged(true);
    }
  }

  function onAutoUpdate(e) {
    saveGlobalPrefs({ autoUpdate: e.target.checked });
  }

  function onTrackUsage(e) {
    saveGlobalPrefs({ trackUsage: e.target.checked });
  }

  return (
    <View>
      <View>
        <Title name="General" />

        {!Platform.isBrowser && (
          <View
            style={{
              flexDirection: 'row',
              maxWidth: 550,
              alignItems: 'center',
              overflow: 'hidden'
            }}
          >
            <Text style={{ flexShrink: 0 }}>Store files here: </Text>
            <Text
              innerRef={dirScrolled}
              style={{
                backgroundColor: 'white',
                padding: '7px 10px',
                borderRadius: 4,
                marginLeft: 5,
                overflow: 'auto',
                whiteSpace: 'nowrap',
                // TODO: When we update electron, we should be able to
                // remove this. In previous versions of Chrome, once the
                // scrollbar appears it never goes away
                '::-webkit-scrollbar': { display: 'none' }
              }}
            >
              {globalPrefs.documentDir}
            </Text>
            <Button
              primary
              onClick={onChooseDocumentDir}
              style={{
                fontSize: 14,
                marginLeft: 5,
                flexShrink: 0,
                alignSelf: 'flex-start'
              }}
            >
              Change location
            </Button>
          </View>
        )}

        {documentDirChanged && (
          <Information style={{ marginTop: 10 }}>
            A restart is required for this change to take effect
          </Information>
        )}

        <View
          style={{
            flexDirection: 'row',
            marginTop: 30,
            alignItems: 'flex-start'
          }}
        >
          <input
            type="checkbox"
            checked={globalPrefs.autoUpdate}
            style={{ marginRight: 5 }}
            onChange={onAutoUpdate}
          />

          <View>
            <Text style={{ fontSize: 15 }}>
              Automatically check for updates
            </Text>
            <View
              style={{
                color: colors.n2,
                marginTop: 10,
                maxWidth: 600,
                lineHeight: '1.4em'
              }}
            >
              By default, Actual will automatically apply new updates as they
              are available. Disabling this will avoid updating Actual. You will
              need to go to the About menu to manually check for updates.
            </View>
          </View>
        </View>
      </View>

      <View style={{ marginTop: 30 }}>
        <Title name="Privacy" />

        <View
          style={{
            flexDirection: 'row',
            marginTop: 30,
            alignItems: 'flex-start'
          }}
        >
          <input
            type="checkbox"
            checked={globalPrefs.trackUsage}
            style={{ marginRight: 5 }}
            onChange={onTrackUsage}
          />

          <View>
            <Text style={{ fontSize: 15 }}>
              Send basic usage statistics back to Actual{"'"}s servers
            </Text>
            <View
              style={{
                color: colors.n2,
                marginTop: 10,
                maxWidth: 600,
                lineHeight: '1.4em'
              }}
            >
              We don{"'"}t track anything specific &mdash; only the fact that
              you{"'"}ve opened Actual. This helps by giving us important
              feedback about how popular new features are.
            </View>
          </View>
        </View>
      </View>
    </View>
  );
}

function FileSettings({
  savePrefs,
  prefs,
  userData,
  localServerURL,
  pushModal,
  resetSync,
  setAppState,
  signOut
}) {
  function onDateFormat(e) {
    let format = e.target.value;
    savePrefs({ dateFormat: format });
  }

  function onNumberFormat(e) {
    let format = e.target.value;
    savePrefs({ numberFormat: format });
  }

  function onChangeKey() {
    pushModal('create-encryption-key', { recreate: true });
  }

  async function onExport() {
    let data = await send('export-budget');
    window.Actual.saveFile(data, `${prefs.id}.zip`, 'Export budget');
  }

  let dateFormat = prefs.dateFormat || 'MM/dd/yyyy';
  let numberFormat = prefs.numberFormat || 'comma-dot';

  return (
    <View>
      <View style={{ marginTop: 30 }}>
        <Title name="Formatting" />

        <Text>
          Date format:{' '}
          <select
            {...css({ marginLeft: 5, fontSize: 14 })}
            onChange={onDateFormat}
          >
            {dateFormats.map(f => (
              <option value={f.value} selected={f.value === dateFormat}>
                {f.label}
              </option>
            ))}
          </select>
        </Text>

        <Text style={{ marginTop: 20 }}>
          Number format:{' '}
          <select
            {...css({ marginLeft: 5, fontSize: 14 })}
            onChange={onNumberFormat}
          >
            {numberFormats.map(f => (
              <option value={f.value} selected={f.value === numberFormat}>
                {f.label}
              </option>
            ))}
          </select>
        </Text>
      </View>

      <View style={{ marginTop: 30 }}>
        <Title name="Encryption" />
        <View style={{ flexDirection: 'row' }}>
          <View>
            <Text style={{ fontWeight: 700, fontSize: 15 }}>
              End-to-end encryption
            </Text>
            <View
              style={{
                color: colors.n2,
                marginTop: 10,
                maxWidth: 600,
                lineHeight: '1.4em'
              }}
            >
              {prefs.encryptKeyId ? (
                <Text>
                  <Text style={{ color: colors.g4, fontWeight: 600 }}>
                    Encryption is turned on.
                  </Text>{' '}
                  Your data is encrypted with a key that only you have before
                  sending it out to the cloud . Local data remains unencrypted
                  so if you forget your password you can re-encrypt it.
                  <Button
                    style={{ marginTop: 10 }}
                    onClick={() => onChangeKey()}
                  >
                    Generate new key
                  </Button>
                </Text>
              ) : (
                <View style={{ alignItems: 'flex-start' }}>
                  <Text style={{ lineHeight: '1.4em' }}>
                    Encryption is not enabled. Any data on our servers is still
                    stored safely and securely, but it's not end-to-end
                    encrypted which means we have the ability to read it (but we
                    won't). If you want, you can use a password to encrypt your
                    data on our servers.
                  </Text>
                  <Button
                    style={{ marginTop: 10 }}
                    onClick={() => {
                      alert(
                        'End-to-end encryption is not supported on the self-hosted service yet'
                      );
                      // pushModal('create-encryption-key');
                    }}
                  >
                    Enable encryption
                  </Button>
                </View>
              )}
            </View>
          </View>
        </View>
      </View>

      <View style={{ marginTop: 30, alignItems: 'flex-start' }}>
        <Title name="Export" />
        <Button onClick={onExport}>Export data</Button>
      </View>

      <Advanced
        prefs={prefs}
        userData={userData}
        pushModal={pushModal}
        resetSync={resetSync}
      />
    </View>
  );
}

function SettingsLink({ to, name, style, first, last }) {
  return (
    <AnchorLink
      to={to}
      style={[
        {
          fontSize: 14,
          padding: '6px 10px',
          borderBottom: '2px solid transparent',
          textDecoration: 'none',
          borderRadius: first ? '4px 0 0 4px' : last ? '0 4px 4px 0' : 4,
          border: '1px solid ' + colors.n4,
          color: colors.n3
        },
        style
      ]}
      activeStyle={{
        backgroundColor: colors.p6,
        borderColor: colors.p6,
        color: 'white'
      }}
    >
      {name}
    </AnchorLink>
  );
}

function Version() {
  const version = useServerVersion();

  return (
    <Text
      style={[
        {
          alignSelf: 'center',
          color: colors.n7,
          ':hover': { color: colors.n2 },
          padding: '6px 10px'
        },
        styles.staticText,
        styles.smallText
      ]}
    >
      {`App: v${window.Actual.ACTUAL_VERSION} | Server: ${version}`}
    </Text>
  );
}

class Settings extends React.Component {
  componentDidMount() {
    this.unlisten = listen('prefs-updated', () => {
      this.props.loadPrefs();
    });

    this.props.getUserData();
    this.props.loadPrefs();
  }

  componentWillUnmount() {
    this.unlisten();
  }

  render() {
    let { prefs, globalPrefs, localServerURL, userData, match } = this.props;

    return (
      <View style={[styles.page, { overflow: 'hidden', fontSize: 14 }]}>
        <View
          style={{
            flexDirection: 'row',
            alignSelf: 'center',
            margin: '15px 0 5px 0'
          }}
        >
          <SettingsLink to={`${match.path}/file`} name="File" first={true} />
          <SettingsLink to={`${match.path}/global`} name="Global" last={true} />
        </View>
        <View
          style={{
            flexDirection: 'row',
            alignSelf: 'center',
            margin: '0 0 10px 0'
          }}
        >
          <Version />
        </View>

        <View
          style={[
            styles.pageContent,
            {
              alignItems: 'flex-start',
              flex: 1,
              overflow: 'auto',
              paddingBottom: 20
            }
          ]}
        >
          <View style={{ flexShrink: 0 }}>
            <Switch>
              <Route path={`${match.path}/`} exact>
                <Redirect to={`${match.path}/file`} />
              </Route>
              <Route path={`${match.path}/global`}>
                <GlobalSettings
                  globalPrefs={globalPrefs}
                  userData={userData}
                  saveGlobalPrefs={this.props.saveGlobalPrefs}
                  pushModal={this.props.pushModal}
                  closeBudget={this.props.closeBudget}
                />
              </Route>
              <Route path={`${match.path}/file`}>
                <FileSettings
                  prefs={prefs}
                  localServerURL={localServerURL}
                  userData={userData}
                  pushModal={this.props.pushModal}
                  savePrefs={this.props.savePrefs}
                  setAppState={this.props.setAppState}
                  signOut={this.props.signOut}
                  resetSync={this.props.resetSync}
                />
              </Route>
            </Switch>
          </View>
        </View>
      </View>
    );
  }
}

export default connect(
  state => ({
    prefs: state.prefs.local,
    globalPrefs: state.prefs.global,
    localServerURL: state.account.localServerURL,
    userData: state.user.data
  }),
  actions
)(Settings);