/* eslint-disable @typescript-eslint/indent */
/* eslint-disable react/require-default-props */
/* eslint-disable react/no-unused-prop-types */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable no-nested-ternary */
/* eslint-disable react/no-unstable-nested-components */
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import Toast from '@arcblock/ux/lib/Toast';
import type { Paginated, TInvoiceExpanded, TSubscription } from '@blocklet/payment-types';
import { OpenInNewOutlined } from '@mui/icons-material';
import { Box, Button, CircularProgress, Stack, Typography, Tooltip, Avatar } from '@mui/material';
import { styled } from '@mui/system';
import { useInfiniteScroll, useRequest, useSetState } from 'ahooks';
import React, { useEffect, useRef, useState } from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import { useNavigate } from 'react-router-dom';

import debounce from 'lodash/debounce';
import Status from '../../components/status';
import { usePaymentContext } from '../../contexts/payment';
import { useSubscription } from '../../hooks/subscription';
import api from '../../libs/api';
import {
  formatBNStr,
  formatError,
  formatToDate,
  formatToDatetime,
  getInvoiceDescriptionAndReason,
  getInvoiceStatusColor,
  getTxLink,
  isCrossOrigin,
} from '../../libs/util';
import Table from '../../components/table';
import { createLink, handleNavigation, LinkInfo } from '../../libs/navigation';

type Result = Paginated<TInvoiceExpanded> & { subscription: TSubscription };

const groupByDate = (items: TInvoiceExpanded[]) => {
  const grouped: { [key: string]: TInvoiceExpanded[] } = {};
  items.forEach((item) => {
    const date = new Date(item.created_at).toLocaleDateString();
    if (!grouped[date]) {
      grouped[date] = [];
    }
    grouped[date]?.push(item);
  });
  return grouped;
};

const fetchData = (params: Record<string, any> = {}): Promise<Result> => {
  const search = new URLSearchParams();
  Object.keys(params).forEach((key) => {
    if (params[key]) {
      search.set(key, String(params[key]));
    }
  });
  return api.get(`/api/invoices?${search.toString()}`).then((res: any) => res.data);
};

type Props = {
  customer_id?: string;
  subscription_id?: string;
  currency_id?: string;
  include_staking?: boolean;
  include_return_staking?: boolean;
  include_recovered_from?: boolean;
  status?: string;
  pageSize?: number;
  target?: string;
  action?: string;
  type?: 'list' | 'table';
  onTableDataChange?: Function;
  relatedSubscription?: boolean;
};

const getInvoiceLink = (invoice: TInvoiceExpanded, action?: string) => {
  if (invoice.id.startsWith('in_')) {
    const path = `/customer/invoice/${invoice.id}${invoice.status === 'uncollectible' && action ? `?action=${action}` : ''}`;
    return {
      connect: invoice.status === 'uncollectible',
      link: createLink(path),
    };
  }

  return {
    connect: false,
    link: createLink(getTxLink(invoice.paymentMethod, invoice.metadata?.payment_details).link, true),
  };
};

const linkStyle = {
  cursor: 'pointer',
};

const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) => void }) => {
  const {
    pageSize,
    target,
    action,
    onPay,
    status,
    customer_id,
    currency_id,
    subscription_id,
    include_staking,
    include_return_staking,
    include_recovered_from,
    onTableDataChange,
    relatedSubscription,
  } = props;
  const listKey = 'invoice-table';
  const { t, locale } = useLocaleContext();
  const navigate = useNavigate();

  const [search, setSearch] = useState<{ pageSize: number; page: number }>({
    pageSize: pageSize || 10,
    page: 1,
  });
  const {
    loading,
    data = { list: [], count: 0 },
    refresh,
  } = useRequest(
    () =>
      fetchData({
        ...search,
        status,
        customer_id,
        currency_id,
        subscription_id,
        include_staking,
        include_return_staking,
        include_recovered_from,
        ignore_zero: true,
      }),
    {
      refreshDeps: [search, status, customer_id, currency_id, subscription_id, include_staking, include_recovered_from],
    }
  );

  const prevData = useRef(data);

  useEffect(() => {
    if (onTableDataChange) {
      onTableDataChange(data, prevData.current);
      prevData.current = data;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data]);

  const subscription = useSubscription('events');

  const debouncedHandleInvoicePaid = debounce(
    async () => {
      Toast.close();
      Toast.success(t('payment.customer.invoice.paySuccess'));
      await refresh();
    },
    1000,
    {
      leading: false,
      trailing: true,
      maxWait: 5000,
    }
  );

  useEffect(() => {
    if (subscription && customer_id) {
      subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
        if (response.customer_id === customer_id) {
          debouncedHandleInvoicePaid();
        }
      });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [subscription]);

  const handleLinkClick = (e: React.MouseEvent, invoice: TInvoiceExpanded) => {
    const { link } = getInvoiceLink(invoice, action);
    handleNavigation(e, link, navigate, { target: link.external ? '_blank' : target });
  };

  const handleRelatedSubscriptionClick = (e: React.MouseEvent, invoice: TInvoiceExpanded) => {
    if (invoice.subscription_id) {
      handleNavigation(e, createLink(`/customer/subscription/${invoice.subscription_id}`), navigate);
    }
  };

  const columns = [
    {
      label: t('common.amount'),
      name: 'total',
      width: 80,
      align: 'right',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;
          const isVoid = invoice.status === 'void';
          return (
            <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
              <Typography sx={isVoid ? { textDecoration: 'line-through' } : {}}>
                {formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
                {invoice.paymentCurrency.symbol}
              </Typography>
            </Box>
          );
        },
      },
    },
    {
      label: t('common.paymentMethod'),
      name: 'paymentMethod',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;
          return (
            <Typography
              sx={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}
              onClick={(e) => handleLinkClick(e, invoice)}>
              <Avatar src={invoice.paymentMethod.logo} sx={{ width: 18, height: 18, mr: 1 }} />
              {invoice.paymentMethod.name}
            </Typography>
          );
        },
      },
    },
    {
      label: t('common.type'),
      name: 'billing_reason',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const invoice = data.list[index] as TInvoiceExpanded;
          return (
            <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
              <Status label={getInvoiceDescriptionAndReason(invoice, locale)?.type} />
            </Box>
          );
        },
      },
    },
    {
      label: t('payment.customer.invoice.invoiceNumber'),
      name: 'number',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;
          return (
            <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
              {invoice?.number}
            </Box>
          );
        },
      },
    },
    ...(relatedSubscription
      ? [
          {
            label: t('common.relatedSubscription'),
            name: 'subscription',
            options: {
              customBodyRenderLite: (_: string, index: number) => {
                const invoice = data?.list[index] as TInvoiceExpanded;
                return invoice.subscription_id ? (
                  <Box
                    onClick={(e) => handleRelatedSubscriptionClick(e, invoice)}
                    sx={{ color: 'text.link', cursor: 'pointer' }}>
                    {invoice.subscription?.description}
                  </Box>
                ) : (
                  <Box onClick={(e) => handleLinkClick(e, invoice)} sx={{ ...linkStyle, color: 'text.lighter' }}>
                    {t('common.none')}
                  </Box>
                );
              },
            },
          },
        ]
      : []),
    {
      label: t('common.updatedAt'),
      name: 'name',
      options: {
        customBodyRenderLite: (val: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;

          return (
            <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
              {formatToDate(
                invoice.created_at,
                locale,
                relatedSubscription ? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD HH:mm:ss'
              )}
            </Box>
          );
        },
      },
    },

    {
      label: t('common.description'),
      name: '',
      options: {
        sort: false,
        customBodyRenderLite: (val: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;

          return (
            <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
              {getInvoiceDescriptionAndReason(invoice, locale)?.description || invoice.id}
            </Box>
          );
        },
      },
    },
    {
      label: t('common.status'),
      name: 'status',
      options: {
        customBodyRenderLite: (val: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;
          const hidePay = invoice.billing_reason === 'overdraft-protection';
          const { connect } = getInvoiceLink(invoice, action);
          const isVoid = invoice.status === 'void';

          if (action && !hidePay) {
            return connect ? (
              <Button variant="text" size="small" onClick={() => onPay(invoice.id)} sx={{ color: 'text.link' }}>
                {t('payment.customer.invoice.pay')}
              </Button>
            ) : (
              <Button
                component="a"
                variant="text"
                size="small"
                onClick={(e) => handleLinkClick(e, invoice)}
                sx={{ color: 'text.link' }}
                rel="noreferrer">
                {t('payment.customer.invoice.pay')}
              </Button>
            );
          }
          return (
            <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
              {isVoid ? (
                <Tooltip title={t('payment.customer.invoice.noPaymentRequired')} arrow placement="top">
                  <span>
                    <Status label={invoice.status} color={getInvoiceStatusColor(invoice.status)} />
                  </span>
                </Tooltip>
              ) : (
                <Status label={invoice.status} color={getInvoiceStatusColor(invoice.status)} />
              )}
            </Box>
          );
        },
      },
    },
  ];

  const onTableChange = ({ page, rowsPerPage }: any) => {
    if (search.pageSize !== rowsPerPage) {
      setSearch((x) => ({ ...x, pageSize: rowsPerPage, page: 1 }));
    } else if (search.page !== page + 1) {
      setSearch((x) => ({ ...x, page: page + 1 }));
    }
  };

  return (
    <InvoiceTableRoot>
      <Table
        hasRowLink
        durable={`__${listKey}__`}
        durableKeys={['page', 'rowsPerPage', 'searchText']}
        data={data.list}
        columns={columns}
        options={{
          count: data.count,
          page: search.page - 1,
          rowsPerPage: search.pageSize,
        }}
        loading={loading}
        onChange={onTableChange}
        toolbar={false}
        sx={{ mt: 2 }}
        showMobile={false}
        mobileTDFlexDirection="row"
        emptyNodeText={t('payment.customer.invoice.emptyList')}
      />
    </InvoiceTableRoot>
  );
});

const InvoiceTableRoot = styled(Box)`
  @media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
    .MuiTable-root > .MuiTableBody-root > .MuiTableRow-root > td.MuiTableCell-root {
      > div {
        width: fit-content;
        flex: inherit;
        font-size: 14px;
      }
    }
    .invoice-summary {
      padding-right: 20px;
    }
  }
`;

const InvoiceList = React.memo((props: Props & { onPay: (invoiceId: string) => void }) => {
  const {
    customer_id,
    subscription_id,
    include_recovered_from,
    currency_id,
    include_staking,
    status,
    pageSize,
    target,
    action,
    onPay,
    onTableDataChange,
  } = props;
  const size = pageSize || 10;

  const subscription = useSubscription('events');
  const { t, locale } = useLocaleContext();
  const navigate = useNavigate();

  const { data, loadMore, loadingMore, loading, reloadAsync } = useInfiniteScroll<Result>(
    (d) => {
      const page = d ? Math.ceil(d.list.length / size) + 1 : 1;
      return fetchData({
        page,
        pageSize: size,
        status,
        customer_id,
        currency_id,
        subscription_id,
        include_staking,
        include_recovered_from,
        ignore_zero: true,
      });
    },
    {
      reloadDeps: [customer_id, subscription_id, status, include_staking, include_recovered_from],
    }
  );

  const prevData = useRef(data);

  useEffect(() => {
    if (onTableDataChange) {
      onTableDataChange(data, prevData.current);
      prevData.current = data;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data]);

  const debouncedHandleInvoicePaid = debounce(
    async () => {
      Toast.close();
      Toast.success(t('payment.customer.invoice.paySuccess'));
      await reloadAsync();
    },
    1000,
    {
      leading: false,
      trailing: true,
      maxWait: 5000,
    }
  );
  // Listen to invoice.paid event and refresh data
  useEffect(() => {
    if (subscription && customer_id) {
      subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
        if (response.customer_id === customer_id) {
          debouncedHandleInvoicePaid();
        }
      });
    }
  }, [subscription]); // eslint-disable-line react-hooks/exhaustive-deps

  if (loading || !data) {
    return <CircularProgress />;
  }

  if (data && data.list.length === 0) {
    if (data.subscription && ['active', 'trialing'].includes(data.subscription.status)) {
      return (
        <Typography
          sx={{
            color: 'text.secondary',
            my: 0.5,
          }}>
          {t('payment.customer.invoice.next', { date: formatToDatetime(data.subscription.current_period_end * 1000) })}
        </Typography>
      );
    }

    return (
      <Typography
        sx={{
          color: 'text.secondary',
          my: 0.5,
        }}>
        {t('payment.customer.invoice.empty')}
      </Typography>
    );
  }

  const hasMore = data && data.list.length < data.count;

  const grouped = groupByDate(data.list as any);

  const handleLinkClick = (e: React.MouseEvent, link: LinkInfo) => {
    handleNavigation(e, link, navigate, { target: link.external ? '_blank' : target });
  };

  return (
    <Root direction="column" gap={1} sx={{ mt: 1 }}>
      {Object.entries(grouped).map(([date, invoices]) => (
        <Box key={date}>
          <Typography sx={{ fontWeight: 'bold', color: 'text.secondary', mt: 2, mb: 1 }}>{date}</Typography>
          {invoices.map((invoice) => {
            const { link, connect } = getInvoiceLink(invoice, action);
            const isVoid = invoice.status === 'void';
            return (
              <Stack
                key={invoice.id}
                direction="row"
                sx={{
                  gap: {
                    xs: 0.5,
                    sm: 1.5,
                    md: 3,
                  },

                  alignItems: 'center',
                  flexWrap: 'nowrap',
                  my: 1,
                }}>
                <Box
                  sx={{
                    flex: 2,
                  }}>
                  <a
                    href={link.url}
                    target={link.external ? '_blank' : target}
                    rel="noreferrer"
                    onClick={(e) => handleLinkClick(e, link)}>
                    <Stack
                      direction="row"
                      spacing={0.5}
                      sx={{
                        alignItems: 'center',
                      }}>
                      <Typography component="span">{invoice.number}</Typography>
                      {link.external && (
                        <OpenInNewOutlined
                          fontSize="small"
                          sx={{
                            color: 'text.secondary',
                            display: { xs: 'none', md: 'inline-flex' },
                          }}
                        />
                      )}
                    </Stack>
                  </a>
                </Box>
                <Box
                  sx={{
                    flex: 1,
                    textAlign: 'right',
                  }}>
                  <Typography sx={isVoid ? { textDecoration: 'line-through' } : {}}>
                    {formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
                    {invoice.paymentCurrency.symbol}
                  </Typography>
                </Box>
                <Box
                  sx={{
                    flex: 1,
                    textAlign: 'right',
                  }}>
                  <Typography>{formatToDate(invoice.created_at, locale, 'HH:mm:ss')}</Typography>
                </Box>
                {!action && (
                  <Box
                    className="invoice-description"
                    sx={{
                      flex: 2,
                      textAlign: 'right',
                      display: { xs: 'none', lg: 'inline-flex' },
                    }}>
                    <Typography>{invoice.description || invoice.id}</Typography>
                  </Box>
                )}
                <Box
                  sx={{
                    flex: 1,
                    textAlign: 'right',
                  }}>
                  {action ? (
                    connect ? (
                      <Button
                        variant="contained"
                        color="primary"
                        size="small"
                        onClick={() => onPay(invoice.id)}
                        sx={{ whiteSpace: 'nowrap' }}>
                        {t('payment.customer.invoice.pay')}
                      </Button>
                    ) : (
                      <Button
                        component="a"
                        variant="contained"
                        size="small"
                        onClick={(e) => handleLinkClick(e, link)}
                        sx={{ whiteSpace: 'nowrap' }}
                        rel="noreferrer">
                        {t('payment.customer.invoice.pay')}
                      </Button>
                    )
                  ) : isVoid ? (
                    <Tooltip title={t('payment.customer.invoice.noPaymentRequired')} arrow placement="top">
                      <span>
                        <Status label={invoice.status} color={getInvoiceStatusColor(invoice.status)} />
                      </span>
                    </Tooltip>
                  ) : (
                    <Status label={invoice.status} color={getInvoiceStatusColor(invoice.status)} />
                  )}
                </Box>
              </Stack>
            );
          })}
        </Box>
      ))}
      <Box>
        {hasMore && (
          <Button variant="text" type="button" color="inherit" onClick={loadMore} disabled={loadingMore}>
            {loadingMore
              ? t('common.loadingMore', { resource: t('payment.customer.invoices') })
              : t('common.loadMore', { resource: t('payment.customer.invoices') })}
          </Button>
        )}
        {!hasMore && data.count > size && (
          <Typography
            sx={{
              color: 'text.secondary',
            }}>
            {t('common.noMore', { resource: t('payment.customer.invoices') })}
          </Typography>
        )}
      </Box>
    </Root>
  );
});

export default function CustomerInvoiceList(rawProps: Props) {
  const props = Object.assign(
    {
      customer_id: '',
      subscription_id: '',
      currency_id: '',
      include_staking: false,
      include_recovered_from: false,
      status: 'open,paid,uncollectible',
      pageSize: 10,
      target: '_self',
      action: '',
      type: 'list',
      onTableDataChange: () => {},
      relatedSubscription: false,
    },
    rawProps
  );
  // eslint-disable-next-line react/prop-types
  const { action, type } = props;
  const { t, locale } = useLocaleContext();
  const { connect } = usePaymentContext();
  const [state, setState] = useSetState({ paying: '' });

  const onPay = (invoiceId: string) => {
    if (state.paying) {
      return;
    }

    setState({ paying: invoiceId });
    connect.open({
      action: 'collect',
      saveConnect: false,
      locale: locale as 'en' | 'zh',
      useSocket: isCrossOrigin() === false,
      messages: {
        scan: '',
        title: t(`payment.customer.invoice.${action || 'pay'}`),
        success: t(`payment.customer.invoice.${action || 'pay'}Success`),
        error: t(`payment.customer.invoice.${action || 'pay'}Error`),
        confirm: '',
      } as any,
      extraParams: { invoiceId, action },
      onSuccess: () => {
        connect.close();
        setState({ paying: '' });
      },
      onClose: () => {
        connect.close();
        setState({ paying: '' });
      },
      onError: (err: any) => {
        setState({ paying: '' });
        Toast.error(formatError(err));
      },
    });
  };

  if (type === 'table') {
    return <InvoiceTable {...props} onPay={onPay} />;
  }
  return <InvoiceList {...props} onPay={onPay} />;
}

const Root = styled(Stack)`
  @media (max-width: ${({ theme }) => theme.breakpoints.values.md}px) {
    svg.MuiSvgIcon-root {
      display: none !important;
    }
  }

  a.MuiButton-root {
    text-decoration: none !important;
  }
`;
