import React, { useState, useMemo, useCallback } from 'react';
import { RectButton } from 'react-native-gesture-handler';
import { useDispatch } from 'react-redux';
import { useLocation, useHistory } from 'react-router';
import { withRouter } from 'react-router-dom';

import { css } from 'glamor';

import { closeBudget } from 'loot-core/src/client/actions/budgets';
import Platform from 'loot-core/src/client/platform';
import PiggyBank from 'loot-design/src/svg/v1/PiggyBank';

import { styles, colors } from '../style';
import Add from '../svg/v1/Add';
import ChevronRight from '../svg/v1/CheveronRight';
import Cog from '../svg/v1/Cog';
import DotsHorizontalTriple from '../svg/v1/DotsHorizontalTriple';
import Reports from '../svg/v1/Reports';
import Wallet from '../svg/v1/Wallet';
import Wrench from '../svg/v1/Wrench';
import ArrowButtonLeft1 from '../svg/v2/ArrowButtonLeft1';
import CalendarIcon from '../svg/v2/Calendar';
import {
  View,
  Block,
  AlignedText,
  AnchorLink,
  ButtonLink,
  Button,
  Menu,
  Tooltip
} from './common';
import { useDraggable, useDroppable, DropHighlight } from './sort.js';
import CellValue from './spreadsheet/CellValue';

export const SIDEBAR_WIDTH = 240;

function Item({
  children,
  icon,
  title,
  style,
  indent = 0,
  to,
  exact,
  onClick,
  button,
  forceHover = false,
  forceActive = false
}) {
  const hoverStyle = {
    backgroundColor: colors.n2
  };
  const activeStyle = {
    borderLeft: '4px solid ' + colors.p8,
    paddingLeft: 19 + indent - 4,
    color: colors.p8
  };
  const linkStyle = [
    {
      ...styles.mediumText,
      paddingTop: 9,
      paddingBottom: 9,
      paddingLeft: 19 + indent,
      paddingRight: 10,
      textDecoration: 'none',
      color: colors.n9,
      ...(forceHover ? hoverStyle : {}),
      ...(forceActive ? activeStyle : {})
    },
    { ':hover': hoverStyle }
  ];

  const content = (
    <View
      style={{
        flexDirection: 'row',
        alignItems: 'center',
        height: 20
      }}
    >
      {icon}
      <Block style={{ marginLeft: 8 }}>{title}</Block>
      <View style={{ flex: 1 }} />
      {button}
    </View>
  );

  return (
    <View style={[{ flexShrink: 0 }, style]}>
      {onClick ? (
        <RectButton onClick={onClick}>
          <View style={linkStyle}>{content}</View>
        </RectButton>
      ) : (
        <AnchorLink
          style={linkStyle}
          to={to}
          exact={exact}
          activeStyle={activeStyle}
        >
          {content}
        </AnchorLink>
      )}

      {children ? <View style={{ marginTop: 5 }}>{children}</View> : null}
    </View>
  );
}

let accountNameStyle = [
  {
    marginTop: -2,
    marginBottom: 2,
    paddingTop: 4,
    paddingBottom: 4,
    paddingRight: 15,
    paddingLeft: 10,
    textDecoration: 'none',
    color: colors.n9
  },
  { ':hover': { backgroundColor: colors.n2 } },
  styles.smallText
];

function Account({
  name,
  account,
  connected,
  failed,
  updated,
  to,
  query,
  style,
  outerStyle,
  onDragChange,
  onDrop
}) {
  let type = account
    ? account.closed
      ? 'account-closed'
      : account.offbudget
      ? 'account-offbudget'
      : 'account-onbudget'
    : 'title';

  let { dragRef } = useDraggable({
    type,
    onDragChange,
    item: { id: account && account.id },
    canDrag: account != null
  });

  let { dropRef, dropPos } = useDroppable({
    types: account ? [type] : [],
    id: account && account.id,
    onDrop: onDrop
  });

  return (
    <View innerRef={dropRef} style={[{ flexShrink: 0 }, outerStyle]}>
      <View>
        <DropHighlight pos={dropPos} />
        <View innerRef={dragRef}>
          <AnchorLink
            ref={dragRef}
            to={to}
            style={[
              accountNameStyle,
              style,
              { position: 'relative', borderLeft: '4px solid transparent' },
              updated && { fontWeight: 700 }
            ]}
            activeStyle={{
              borderColor: colors.p8,
              color: colors.p8,
              // This is kind of a hack, but we don't ever want the account
              // that the user is looking at to be "bolded" which means it
              // has unread transactions. The system does mark is read and
              // unbolds it, but it still "flashes" bold so this just
              // ignores it if it's active
              fontWeight: 'normal',
              '& .dot': {
                backgroundColor: colors.p8,
                transform: 'translateX(-4.5px)'
              }
            }}
          >
            <View
              style={{
                position: 'absolute',
                left: 0,
                top: 0,
                bottom: 0,
                flexDirection: 'row',
                alignItems: 'center'
              }}
            >
              <div
                className="dot"
                {...css({
                  marginRight: 3,
                  width: 5,
                  height: 5,
                  borderRadius: 5,
                  backgroundColor: failed ? colors.r7 : colors.g5,
                  marginLeft: 2,
                  transition: 'transform .3s',
                  opacity: connected ? 1 : 0
                })}
              />
            </View>

            <AlignedText
              left={name}
              right={
                <CellValue debug={true} binding={query} type="financial" />
              }
            />
          </AnchorLink>
        </View>{' '}
      </View>
    </View>
  );
}

function Accounts({
  accounts,
  failedAccounts,
  updatedAccounts,
  getAccountPath,
  budgetedAccountPath,
  offBudgetAccountPath,
  getBalanceQuery,
  getOnBudgetBalance,
  getOffBudgetBalance,
  showClosedAccounts,
  onAddAccount,
  onToggleClosedAccounts,
  onReorder
}) {
  let [isDragging, setIsDragging] = useState(false);
  let offbudgetAccounts = useMemo(
    () =>
      accounts.filter(
        account => account.closed === 0 && account.offbudget === 1
      ),
    [accounts]
  );
  let budgetedAccounts = useMemo(
    () =>
      accounts.filter(
        account => account.closed === 0 && account.offbudget === 0
      ),
    [accounts]
  );
  let closedAccounts = useMemo(
    () => accounts.filter(account => account.closed === 1),
    [accounts]
  );

  function onDragChange(drag) {
    setIsDragging(drag.state === 'start');
  }

  let makeDropPadding = (i, length) => {
    if (i === 0) {
      return {
        paddingTop: isDragging ? 15 : 0,
        marginTop: isDragging ? -15 : 0
      };
    } else if (i === length - 1) {
      return {
        paddingBottom: 15
      };
    }
    return null;
  };

  return (
    <View>
      {budgetedAccounts.length > 0 && (
        <Account
          name="For budget"
          to={budgetedAccountPath}
          query={getOnBudgetBalance()}
          style={{ marginTop: 15, color: colors.n6 }}
        />
      )}

      {budgetedAccounts.map((account, i) => (
        <Account
          key={account.id}
          name={account.name}
          account={account}
          connected={!!account.bankId}
          failed={failedAccounts && failedAccounts.has(account.id)}
          updated={updatedAccounts && updatedAccounts.includes(account.id)}
          to={getAccountPath(account)}
          query={getBalanceQuery(account)}
          onDragChange={onDragChange}
          onDrop={onReorder}
          outerStyle={makeDropPadding(i, budgetedAccounts.length)}
        />
      ))}

      {offbudgetAccounts.length > 0 && (
        <Account
          name="Off budget"
          to={offBudgetAccountPath}
          query={getOffBudgetBalance()}
          style={{ color: colors.n6 }}
        />
      )}

      {offbudgetAccounts.map((account, i) => (
        <Account
          key={account.id}
          name={account.name}
          account={account}
          connected={!!account.bankId}
          failed={failedAccounts && failedAccounts.has(account.id)}
          updated={updatedAccounts && updatedAccounts.includes(account.id)}
          to={getAccountPath(account)}
          query={getBalanceQuery(account)}
          onDragChange={onDragChange}
          onDrop={onReorder}
          outerStyle={makeDropPadding(i, offbudgetAccounts.length)}
        />
      ))}

      {closedAccounts.length > 0 && (
        <View
          style={[
            accountNameStyle,
            {
              marginTop: 15,
              color: colors.n6,
              flexDirection: 'row',
              userSelect: 'none',
              alignItems: 'center',
              flexShrink: 0
            }
          ]}
          onClick={onToggleClosedAccounts}
        >
          {'Closed Accounts' + (showClosedAccounts ? '' : '...')}
        </View>
      )}

      {showClosedAccounts &&
        closedAccounts.map((account, i) => (
          <Account
            key={account.id}
            name={account.name}
            account={account}
            to={getAccountPath(account)}
            query={getBalanceQuery(account)}
            onDragChange={onDragChange}
            onDrop={onReorder}
          />
        ))}
    </View>
  );
}

function ToggleButton({ style, onFloat }) {
  return (
    <View className="float" style={[style, { flexShrink: 0 }]}>
      <Button bare onClick={onFloat}>
        <ArrowButtonLeft1 style={{ width: 13, height: 13, color: colors.n5 }} />
      </Button>
    </View>
  );
}

const MenuButton = withRouter(function MenuButton({ history }) {
  let dispatch = useDispatch();
  let [menuOpen, setMenuOpen] = useState(false);

  function onMenuSelect(type) {
    setMenuOpen(false);

    switch (type) {
      case 'settings':
        history.push('/settings');
        break;
      case 'help':
        window.open('https://actualbudget.github.io/docs', '_blank');
        break;
      case 'close':
        dispatch(closeBudget());
        break;
      default:
    }
  }

  let items = [
    { name: 'settings', text: 'Settings' },
    { name: 'help', text: 'Help' },
    { name: 'close', text: 'Close File' }
  ];

  return (
    <Button
      bare
      style={{
        color: colors.n5,
        flexShrink: 0
      }}
      activeStyle={{ color: colors.p7 }}
      onClick={() => setMenuOpen(true)}
    >
      <DotsHorizontalTriple
        width={15}
        height={15}
        style={{ color: 'inherit', transform: 'rotateZ(0deg)' }}
      />
      {menuOpen && (
        <Tooltip
          position="bottom-right"
          style={{ padding: 0 }}
          onClose={() => setMenuOpen(false)}
        >
          <Menu onMenuSelect={onMenuSelect} items={items} />
        </Tooltip>
      )}
    </Button>
  );
});

function Tools() {
  let [isOpen, setOpen] = useState(false);
  let location = useLocation();
  let history = useHistory();
  let onToggle = useCallback(() => setOpen(open => !open), []);

  let items = [
    { name: 'payees', text: 'Payees' },
    { name: 'rules', text: 'Rules' },
    { name: 'repair-splits', text: 'Repair split transactions' }
  ];

  let onMenuSelect = useCallback(
    type => {
      switch (type) {
        case 'payees':
          history.push('/payees');
          break;
        case 'rules':
          history.push('/rules');
          break;
        case 'repair-splits':
          history.push('/tools/fix-splits', { locationPtr: history.location });
          break;
        default:
      }
      setOpen(false);
    },
    [history]
  );

  return (
    <View style={{ flexShrink: 0 }}>
      <Item
        title="More Tools"
        icon={<Wrench width={15} height={15} style={{ color: 'inherit' }} />}
        exact={true}
        onClick={onToggle}
        style={{ pointerEvents: isOpen ? 'none' : 'auto' }}
        forceHover={isOpen}
        forceActive={['/payees', '/rules', '/tools'].some(route =>
          location.pathname.startsWith(route)
        )}
        button={
          <ChevronRight
            width={12}
            height={12}
            style={{ color: colors.n6, marginRight: 6 }}
          />
        }
      />
      {isOpen && (
        <Tooltip
          position="right"
          offset={-8}
          style={{ padding: 0 }}
          onClose={onToggle}
        >
          <Menu onMenuSelect={onMenuSelect} items={items} />
        </Tooltip>
      )}
    </View>
  );
}

export function Sidebar({
  style,
  budgetName,
  accounts,
  failedAccounts,
  updatedAccounts,
  getBalanceQuery,
  getOnBudgetBalance,
  getOffBudgetBalance,
  showClosedAccounts,
  isFloating,
  onFloat,
  onAddAccount,
  onToggleClosedAccounts,
  onReorder
}) {
  let hasWindowButtons = !Platform.isBrowser && Platform.OS === 'mac';

  return (
    <View
      style={[
        {
          width: SIDEBAR_WIDTH,
          color: colors.n9,
          backgroundColor: colors.n1,
          '& .float': {
            opacity: 0,
            transition: 'opacity .25s, width .25s',
            width: hasWindowButtons ? null : 0
          },
          '&:hover .float': {
            opacity: 1,
            width: hasWindowButtons ? null : 'auto'
          }
        },
        style
      ]}
    >
      {hasWindowButtons && (
        <ToggleButton
          style={[
            {
              height: isFloating ? 0 : 36,
              alignItems: 'flex-end',
              justifyContent: 'center',
              overflow: 'hidden',
              WebkitAppRegion: 'drag',
              paddingRight: 8
            }
          ]}
          onFloat={onFloat}
        />
      )}
      <View
        style={[
          {
            paddingTop: 35,
            height: 30,
            flexDirection: 'row',
            alignItems: 'center',
            margin: '0 8px 23px 20px',
            transition: 'padding .4s'
          },
          hasWindowButtons && {
            paddingTop: 20,
            justifyContent: 'flex-start'
          }
        ]}
      >
        {budgetName}

        {!Platform.isBrowser && (
          <ButtonLink
            bare
            to="/settings"
            style={{
              // Needed for Windows? No idea why this is displayed as block
              display: 'inherit',
              color: colors.n5,
              marginLeft: hasWindowButtons ? 0 : 5,
              flexShrink: 0
            }}
            activeStyle={{ color: colors.p7 }}
          >
            <Cog width={15} height={15} style={{ color: 'inherit' }} />
          </ButtonLink>
        )}

        <View style={{ flex: 1, flexDirection: 'row' }} />

        {!hasWindowButtons && <ToggleButton onFloat={onFloat} />}
        {Platform.isBrowser && <MenuButton />}
      </View>

      <View style={{ overflow: 'auto' }}>
        <Item
          title="Budget"
          icon={<Wallet width={15} height={15} style={{ color: 'inherit' }} />}
          to="/budget"
        />
        <Item
          title="Reports"
          icon={<Reports width={15} height={15} style={{ color: 'inherit' }} />}
          to="/reports"
        />

        <Item
          title="Schedules"
          icon={
            <CalendarIcon width={15} height={15} style={{ color: 'inherit' }} />
          }
          to="/schedules"
        />

        <Tools />

        <Item
          title="Accounts"
          to="/accounts"
          icon={
            <PiggyBank width={15} height={15} style={{ color: 'inherit' }} />
          }
          exact={true}
          button={
            <Button
              bare
              onClick={e => {
                e.stopPropagation();
                e.preventDefault();
                onAddAccount();
              }}
            >
              <Add width={12} height={12} style={{ color: colors.n6 }} />
            </Button>
          }
        />

        <Accounts
          accounts={accounts}
          failedAccounts={failedAccounts}
          updatedAccounts={updatedAccounts}
          getAccountPath={account => `/accounts/${account.id}`}
          budgetedAccountPath="/accounts/budgeted"
          offBudgetAccountPath="/accounts/offbudget"
          getBalanceQuery={getBalanceQuery}
          getOnBudgetBalance={getOnBudgetBalance}
          getOffBudgetBalance={getOffBudgetBalance}
          showClosedAccounts={showClosedAccounts}
          onAddAccount={onAddAccount}
          onToggleClosedAccounts={onToggleClosedAccounts}
          onReorder={onReorder}
        />
      </View>
    </View>
  );
}