/* eslint-disable no-nested-ternary */
/* eslint-disable react/no-unstable-nested-components */
import Datatable from '@arcblock/ux/lib/Datatable';
import { useCreation, useMemoizedFn, useMount, useReactive, useRequest } from 'ahooks';
import { translate } from '@arcblock/ux/lib/Locale/util';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import RelativeTime from '@arcblock/ux/lib/RelativeTime';
import UAParser from 'ua-parser-js';
import { getVisitorId } from '@arcblock/ux/lib/Util';
import { useConfirm } from '@arcblock/ux/lib/Dialog';
import DidAddress from '@arcblock/ux/lib/DID';
import PQueue from 'p-queue';
import {
  Box,
  Button,
  CircularProgress,
  FormControlLabel,
  Radio,
  RadioGroup,
  Tooltip,
  Typography,
  useMediaQuery,
} from '@mui/material';
import { memo, ReactElement, useEffect } from 'react';
import { UserSession } from '@blocklet/js-sdk';
import { mergeSx } from '@arcblock/ux/lib/Util/style';

import useMobile from '../../hooks/use-mobile';
import UserSessionInfo from './user-session-info';
import { User } from '../../@types';
import { client } from '../../libs/client';
import { translations } from '../libs/locales';
import { ip2Region } from '../libs/utils';

const parseUa = (ua: string) => {
  const parser = new UAParser(ua, {
    // eslint-disable-next-line no-useless-escape
    browser: [[/(ArcWallet)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION]],
  });
  const result = parser.getResult();
  return result;
};

const textRightStyle = {
  display: 'flex',
  justifyContent: 'flex-end',
  textAlign: 'right',
};

const queue = new PQueue({ concurrency: 1 });

const UserSessionIp = memo(({ userSession, isMobile = false }: { userSession: any; isMobile: boolean }) => {
  const currentState = useReactive({
    loading: true,
    ipRegion: '',
  });

  const { t } = useLocaleContext();

  useEffect(() => {
    queue.add(async () => {
      try {
        currentState.ipRegion = await ip2Region(userSession.lastLoginIp);
      } finally {
        currentState.loading = false;
      }
    });
  }, [currentState, userSession.lastLoginIp]);

  return (
    <Box
      {...(isMobile && {
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'flex-end',
        gap: 1,
        flexWrap: 'wrap',
      })}>
      {currentState.ipRegion ? (
        <>
          <Typography variant="body2">{currentState.ipRegion}</Typography>
          <Typography variant="body2" color="grey">
            {userSession.lastLoginIp || t('unknown')}
          </Typography>
        </>
      ) : (
        <>
          <Typography>{userSession.lastLoginIp || t('unknown')}</Typography>
          {currentState.loading ? (
            <Typography variant="body2" color="grey" sx={{ textAlign: 'center' }}>
              <CircularProgress size={12} color="inherit" />
            </Typography>
          ) : null}
        </>
      )}
    </Box>
  );
});

export default function UserSessions({
  user,
  showAction = true,
  showUser = true,
  getUserSessions,
  showOffline = false,
  showAppPid = false,
}: {
  readonly user: User & {
    userSessions?: any[];
  };
  readonly showAction?: boolean;
  readonly showUser?: boolean;
  readonly showOffline?: boolean;
  readonly showAppPid?: boolean;
  readonly getUserSessions: (params: {
    page: number;
    pageSize: number;
    status?: 'online' | 'expired' | 'offline';
  }) => Promise<{
    paging: {
      total: number;
    };
    list: UserSession[];
  }>;
}) {
  const filterParams = useReactive({
    status: 'online',
    page: 1,
    pageSize: 10,
  });
  const userSessionsCountMap = useReactive({
    online: 0,
    expired: 0,
    offline: 0,
  });
  const currentVisitorId = getVisitorId();
  const { locale } = useLocaleContext();
  const isMobile = useMobile({ key: 'md' });
  const isLg = useMediaQuery((theme: any) => theme.breakpoints.down('lg'));
  const { confirmApi, confirmHolder } = useConfirm();
  const t = useMemoizedFn((key, data = {}) => {
    return translate(translations, key, locale, 'en', data);
  });
  const getData = useMemoizedFn(async () => {
    const result = await getUserSessions({
      page: filterParams.page,
      pageSize: filterParams.pageSize,
      status: filterParams.status as 'online' | 'expired' | 'offline',
    });

    const total = result?.paging?.total || 0;
    userSessionsCountMap[filterParams.status as keyof typeof userSessionsCountMap] = total;

    return {
      total,
      list: result?.list || [],
    };
  });

  useMount(async () => {
    const expiredResult = await getUserSessions({
      page: 1,
      pageSize: 1,
      status: 'expired',
    });
    userSessionsCountMap.expired = expiredResult?.paging?.total || 0;

    const offlineResult = await getUserSessions({
      page: 1,
      pageSize: 1,
      status: 'offline',
    });
    userSessionsCountMap.offline = offlineResult?.paging?.total || 0;
  });

  const pageState = useRequest(getData, {
    refreshDeps: [filterParams.status, filterParams.page, filterParams.pageSize],
  });

  const safeData = useCreation(() => {
    return pageState.data?.list || [];
  }, [pageState.data?.list]);

  const disableLogout = useCreation(() => {
    const status = filterParams.status as keyof typeof userSessionsCountMap;
    if (status === 'online') {
      // HACK: 这里只能假设会话列表包含了当前登录的会话，所以只有大于 1 的时候才能够去注销其他会话
      return userSessionsCountMap[status] <= 1;
    }
    return userSessionsCountMap[status] === 0;
  }, [safeData, currentVisitorId]);

  const mergeStyle = useCreation(() => {
    return mergeSx({}, isMobile ? textRightStyle : {});
  }, [isMobile]);

  const logout = useMemoizedFn(({ visitorId }) => {
    confirmApi.open({
      title: t('logoutThisSession'),
      content: t('logoutThisSessionConfirm'),
      confirmButtonText: t('confirm'),
      confirmButtonProps: {
        color: 'error',
      },
      cancelButtonText: t('cancel'),
      onConfirm: async () => {
        await client.user.logout({
          visitorId,
          includeFederated: true,
        });
        // 如果当前页没有数据需要向前翻页
        const { page = 0 } = filterParams;
        const { list = [] } = pageState.data || {};
        const currentPageLength = list.length || 0;
        if (page > 1 && currentPageLength === 1) {
          filterParams.page = Math.max(page - 1, 1);
        }
        pageState.refresh();
        confirmApi.close();
      },
    });
  });
  const logoutAll = useMemoizedFn(() => {
    confirmApi.open({
      title: t('logoutAllSession', {
        type: t(filterParams.status),
      }),
      content: t('logoutAllSessionConfirm', {
        type: t(filterParams.status),
      }),
      confirmButtonText: t('confirm'),
      confirmButtonProps: {
        color: 'error',
      },
      cancelButtonText: t('cancel'),
      onConfirm: async () => {
        await client.user.logout({
          status: filterParams.status,
          visitorId: currentVisitorId as string,
          includeFederated: true,
        });
        // 重置至第一页
        filterParams.page = 1;
        pageState.refresh();
        confirmApi.close();
      },
    });
  });
  const customButtons: ReactElement<any>[] = [];
  if (showAction) {
    customButtons.push(
      <Tooltip
        key="logoutAll"
        title={t('logoutAllTips', {
          type: t(filterParams.status),
        })}>
        <Button
          sx={{ ml: 0.5 }}
          size="small"
          variant="contained"
          color="error"
          onClick={logoutAll}
          disabled={disableLogout}>
          {t('logoutAll', {
            type: t(filterParams.status),
          })}
        </Button>
      </Tooltip>
    );
  }
  const tableOptions = useCreation(() => {
    return {
      // viewColumns: false,
      search: false,
      sort: false,
      download: false,
      filter: false,
      print: false,
      expandableRowsOnClick: false,
      searchDebounceTime: 600,
      page: filterParams.page - 1,
      rowsPerPage: filterParams.pageSize,
      count: pageState.data?.total || 0,
    };
  }, [pageState.data?.total, filterParams.page, filterParams.pageSize]);
  const columns = [
    {
      label: t('platform'),
      name: 'platform',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          const result = parseUa(x?.ua);
          return (
            <Box sx={mergeStyle}>{[result.os?.name, result.os?.version].filter(Boolean).join('/') || t('unknown')}</Box>
          );
        },
      },
    },
    {
      label: t('deviceType'),
      name: 'deviceType',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          const result = parseUa(x?.ua);
          return (
            <Box sx={mergeStyle}>
              {[result.browser?.name, result.browser?.version].filter(Boolean).join('/') || t('unknown')}
            </Box>
          );
        },
      },
    },
    {
      label: t('walletOS'),
      name: 'walletOS',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return <Box sx={mergeStyle}>{x.extra?.walletOS || t('unknown')}</Box>;
        },
      },
    },
    showAppPid && {
      label: 'App Pid',
      name: 'appPid',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return x.appPid ? <DidAddress size={14} responsive={false} compact did={x.appPid} /> : null;
        },
      },
    },
    showUser && {
      label: t('user'),
      name: 'user',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return (
            <Box sx={mergeSx({ minWidth: 150, maxWidth: 250 }, isMobile ? textRightStyle : {})}>
              <UserSessionInfo sessionUser={x.user} user={user} />
            </Box>
          );
        },
      },
    },
    {
      label: t('createdAt'),
      name: 'createdAt',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return (
            <Box sx={mergeStyle}>
              {x.createdAt ? (
                <RelativeTime value={x.createdAt} relativeRange={3 * 86400 * 1000} locale={locale} />
              ) : (
                t('unknown')
              )}
            </Box>
          );
        },
      },
    },
    !showOffline && {
      label: t('updatedAt'),
      name: 'updatedAt',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return (
            <Box sx={mergeStyle}>
              {x.status === 'expired' ? (
                t('expired')
              ) : (
                <RelativeTime value={x.updatedAt} relativeRange={3 * 86400 * 1000} locale={locale} />
              )}
            </Box>
          );
        },
      },
    },
    {
      label: t('lastLoginIp'),
      name: 'lastLoginIp',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return <UserSessionIp userSession={x} isMobile={isMobile} />;
        },
      },
    },
    showAction && {
      label: t('actions'),
      name: 'actions',
      options: {
        customBodyRenderLite: (rawIndex: number) => {
          const x = safeData[rawIndex];
          return (
            <Box sx={mergeStyle}>
              <Button
                sx={{
                  whiteSpace: 'nowrap',
                  fontSize: '12px !important',
                  lineHeight: '1.25',
                  px: 1,
                }}
                disabled={currentVisitorId === x.visitorId}
                variant="outlined"
                size="small"
                color="error"
                onClick={() => logout({ visitorId: x.visitorId })}>
                {x.status === 'expired'
                  ? t('remove')
                  : currentVisitorId === x.visitorId
                    ? t('currentSession')
                    : t('logout')}
              </Button>
            </Box>
          );
        },
      },
    },
  ].filter(Boolean);

  return (
    <Box
      className="pc-user-sessions"
      sx={{
        maxWidth: isMobile ? 'unset' : isLg ? 'calc(100vw - 300px)' : '100%',
        ...(isMobile && {
          '.pc-user-sessions-table > div:nth-child(2)': {
            border: '1px solid',
            borderColor: 'divider',
            borderRadius: 1,
          },
        }),
        '.MuiTableCell-head': {
          whiteSpace: 'nowrap',
          fontWeight: 'bold',
        },
        '.MuiTableRow-root': {
          border: 'unset',
          '&:nth-child(even)': {
            backgroundColor: 'grey.50',
            '&:hover': {
              backgroundColor: (theme) => `${theme.palette.grey[50]} !important`,
            },
          },
        },
        '.MuiTableRow-hover': {
          '&:hover': {
            backgroundColor: 'inherit !important',
          },
        },
        '.MuiTableCell-root': {
          paddingRight: '8px',
          paddingLeft: '8px',
          color: 'text.secondary',
          ...(isMobile && {
            padding: '8px !important',
            '&:first-child': {
              paddingTop: '20px!important',
            },
            '&:last-child': {
              paddingBottom: '20px!important',
            },
          }),
        },
      }}>
      {confirmHolder}
      <Datatable
        count={pageState.data?.total || 0}
        locale={locale}
        data={safeData}
        // @ts-expect-error
        columns={columns}
        // @ts-expect-error
        customButtons={customButtons}
        options={tableOptions}
        loading={pageState.loading}
        className="pc-user-sessions-table"
        title={
          <RadioGroup
            row
            sx={{ lineHeight: 1, ml: 1 }}
            onChange={(e) => {
              filterParams.status = e.target.value;
            }}>
            <FormControlLabel
              value="online"
              control={
                <Radio size="small" sx={{ lineHeight: 1, fontSize: 0 }} checked={filterParams.status === 'online'} />
              }
              label={
                <Typography sx={{ display: 'flex', alignItems: 'center' }}>
                  {t('online')}
                  <Typography component="span" sx={{ ml: 0.5, color: 'text.secondary' }}>
                    ({userSessionsCountMap.online})
                  </Typography>
                </Typography>
              }
            />
            <FormControlLabel
              value="expired"
              control={
                <Radio size="small" sx={{ lineHeight: 1, fontSize: 0 }} checked={filterParams.status === 'expired'} />
              }
              label={
                <Typography sx={{ display: 'flex', alignItems: 'center' }}>
                  {t('expired')}
                  <Typography component="span" sx={{ ml: 0.5, color: 'text.secondary' }}>
                    ({userSessionsCountMap.expired})
                  </Typography>
                </Typography>
              }
            />
            {showOffline ? (
              <FormControlLabel
                value="offline"
                control={
                  <Radio size="small" sx={{ lineHeight: 1, fontSize: 0 }} checked={filterParams.status === 'offline'} />
                }
                label={
                  <Typography sx={{ display: 'flex', alignItems: 'center' }}>
                    {t('offline')}
                    <Typography component="span" sx={{ ml: 0.5, color: 'text.secondary' }}>
                      ({userSessionsCountMap.offline})
                    </Typography>
                  </Typography>
                }
              />
            ) : null}
          </RadioGroup>
        }
        onChange={(state) => {
          filterParams.page = state.page + 1;
          filterParams.pageSize = state.rowsPerPage;
        }}
      />
    </Box>
  );
}
