/* eslint-disable react/no-unstable-nested-components */
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import {
  Status,
  api,
  formatAmount,
  formatExchangeRate,
  formatUsdAmount,
  formatTime,
  getInvoiceStatusColor,
  getUsdAmountFromTokenUnits,
  Table,
  useDefaultPageSize,
  getInvoiceDescriptionAndReason,
  getTxLink,
} from '@blocklet/payment-react';
import type { TInvoiceExpanded } from '@blocklet/payment-types';
import { Avatar, CircularProgress, Stack, Tooltip, Typography } from '@mui/material';
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
import { BN } from '@ocap/util';
import { useEffect, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import { useCacheState } from '../../hooks/cache-state';
import CustomerLink from '../customer/link';
import FilterToolbar from '../filter-toolbar';
import InvoiceActions from './action';

const getInvoiceQuoteInfo = (invoice: TInvoiceExpanded) => {
  const lines = (invoice as any).lines || [];
  for (const line of lines) {
    const quote = (line.metadata as any)?.quote;
    if (quote?.exchange_rate) {
      return quote;
    }
  }
  return null;
};

const fetchData = (params: Record<string, any> = {}): Promise<{ list: TInvoiceExpanded[]; count: number }> => {
  const search = new URLSearchParams();
  const mergedParams: Record<string, any> = { include_quote: true, ...params };
  Object.keys(mergedParams).forEach((key) => {
    let v = mergedParams[key];
    if (v === undefined || v === null || v === '') {
      return;
    }
    if (key === 'q') {
      v = Object.entries(v)
        .map((x) => x.join(':'))
        .join(' ');
    }
    search.set(key, String(v));
  });

  return api.get(`/api/invoices?${search.toString()}`).then((res: any) => res.data);
};

type SearchProps = {
  status?: string;
  pageSize: number;
  page: number;
  customer_id?: string;
  currency_id?: string;
  subscription_id?: string;
  q?: any;
  o?: string;
  tax_rate_id?: string;
};

type ListProps = {
  features?: {
    customer?: boolean;
    toolbar?: boolean;
    filter?: boolean;
    footer?: boolean;
  };
  customer_id?: string;
  subscription_id?: string;
  status?: string;
  ignore_zero?: boolean;
  include_staking?: boolean;
  include_return_staking?: boolean;
  tax_rate_id?: string;

  mode?: 'admin' | 'customer';
};

const getListKey = (props: ListProps) => {
  if (props.customer_id) {
    return `customer-invoices-${props.customer_id}`;
  }
  if (props.subscription_id) {
    return `subscription-invoices-${props.subscription_id}`;
  }
  if (props.tax_rate_id) {
    return `tax-rate-invoices-${props.tax_rate_id}`;
  }

  return 'invoices';
};

const getInvoiceLink = (invoice: TInvoiceExpanded) => {
  if (invoice.id.startsWith('in_')) {
    return {
      external: false,
      url: `/admin/billing/${invoice.id}`,
    };
  }

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

function InvoiceLink({ invoice, children }: { invoice: TInvoiceExpanded; children: React.ReactNode }) {
  const link = getInvoiceLink(invoice);
  if (link.external) {
    return (
      <a href={link.url} target="_blank" rel="noreferrer">
        {children}
      </a>
    );
  }
  return <Link to={link.url}>{children}</Link>;
}

export default function InvoiceList({
  customer_id = '',
  subscription_id = '',
  tax_rate_id = '',
  features = {
    customer: true,
    filter: true,
  },
  status = '',
  include_staking = false,
  include_return_staking = false,
  ignore_zero = false,
  mode = 'admin',
}: ListProps) {
  const [searchParams] = useSearchParams();
  const listKey = getListKey({ customer_id, subscription_id });

  const { t, locale } = useLocaleContext();
  const defaultPageSize = useDefaultPageSize(20);

  const [search, setSearch] = useCacheState<
    SearchProps & { ignore_zero?: boolean; include_staking?: boolean; include_return_staking?: boolean }
  >(listKey, {
    defaultValue: {
      status: status as string,
      customer_id,
      subscription_id,
      tax_rate_id,
      pageSize: defaultPageSize,
      page: 1,
      ignore_zero: !!ignore_zero,
      include_staking: !!include_staking,
      include_return_staking: !!include_return_staking,
    },
    getUrlParams: () => {
      const params: Record<string, any> = {};
      if (searchParams.has('status')) {
        params.status = searchParams.get('status');
      }
      if (searchParams.has('currency_id')) {
        params.currency_id = searchParams.get('currency_id');
      }
      if (searchParams.has('customer_id')) {
        params.customer_id = searchParams.get('customer_id');
      }
      return params;
    },
  });

  const [data, setData] = useState({}) as any;

  const refresh = () =>
    fetchData({
      ...search,
      include_staking: !!include_staking,
      ignore_zero: !!ignore_zero,
      include_return_staking: !!include_return_staking,
      tax_rate_id,
    }).then((res: any) => {
      setData(res);
    });

  useEffect(() => {
    refresh();
  }, [search]);

  useEffect(() => {
    if (tax_rate_id && search?.tax_rate_id !== tax_rate_id) {
      // @ts-ignore
      setSearch((prev) => ({ ...(prev || {}), tax_rate_id, page: 1 }));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [tax_rate_id]);

  if (!data.list) {
    return <CircularProgress />;
  }
  const columns = [
    {
      label: t('common.amount'),
      name: 'total',
      width: 80,
      align: 'right',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          const quoteInfo = getInvoiceQuoteInfo(item);
          const providers = quoteInfo?.providers || [];
          const providerNames = providers.map((provider: any) => provider.provider_name).filter(Boolean);
          // Unified provider display format: "provider_name (n sources)" for multiple, specific name for single
          let providerDisplay = '—';
          if (providers.length > 1) {
            providerDisplay = `${providerNames[0] || providers[0]?.provider_id || 'data'} (${providers.length} sources)`;
          } else if (providers.length === 1) {
            providerDisplay = providerNames[0] || providers[0]?.provider_id || '—';
          } else {
            providerDisplay = quoteInfo?.rate_provider_name || quoteInfo?.rate_provider_id || '—';
          }
          const consensusMethod = quoteInfo?.consensus_method || (providers.length > 1 ? 'median' : 'single');
          const rateTimestamp = quoteInfo?.rate_timestamp_ms ? formatTime(quoteInfo.rate_timestamp_ms) : '—';
          const rateStatus = quoteInfo?.degraded ? t('customer.quote.degraded') : null;
          const baseAmountLine = quoteInfo?.base_amount
            ? `${quoteInfo.base_amount} ${quoteInfo.base_currency || 'USD'}`
            : null;
          const formattedRate = formatExchangeRate(quoteInfo?.exchange_rate || null);
          const rateLine = formattedRate
            ? (() => {
                const currencyMap = {
                  USD: '$',
                  CNY: '¥',
                };
                const currencySymbol = currencyMap[quoteInfo?.base_currency as keyof typeof currencyMap];
                return `1 ${item.paymentCurrency.symbol} ≈ ${
                  currencySymbol
                    ? `${currencySymbol}${formattedRate}`
                    : `${formattedRate} ${quoteInfo?.base_currency || 'USD'}`
                }`;
              })()
            : null;

          let usdAmount: string | null = null;
          if (quoteInfo?.base_amount) {
            usdAmount = formatUsdAmount(quoteInfo.base_amount, locale);
          } else if (quoteInfo?.exchange_rate && item.total) {
            const calculatedUsd = getUsdAmountFromTokenUnits(
              new BN(item.total),
              item.paymentCurrency.decimal,
              quoteInfo.exchange_rate
            );
            if (calculatedUsd) {
              usdAmount = formatUsdAmount(calculatedUsd, locale);
            }
          }

          const tooltipContent = quoteInfo ? (
            <Stack spacing={0.5}>
              <Typography variant="caption">
                {t('customer.quote.provider')}: {providerDisplay || '—'}
              </Typography>
              {baseAmountLine && (
                <Typography variant="caption">
                  {t('customer.quote.baseAmount')}: {baseAmountLine}
                </Typography>
              )}
              {rateLine && (
                <Typography variant="caption">
                  {t('customer.quote.exchangeRate')}: {rateLine}
                </Typography>
              )}
              <Typography variant="caption">
                {t('customer.quote.rateTimestamp')}: {rateTimestamp}
              </Typography>
              <Typography variant="caption">
                {t('customer.quote.consensusMethod')}: {consensusMethod}
              </Typography>
              {rateStatus && (
                <Typography variant="caption">
                  {t('common.status')}: {rateStatus}
                  {quoteInfo?.degraded_reason ? ` (${quoteInfo.degraded_reason})` : ''}
                </Typography>
              )}
              {quoteInfo.slippage_percent !== null && quoteInfo.slippage_percent !== undefined && (
                <Typography variant="caption">
                  {t('customer.quote.slippage')}: {quoteInfo.slippage_percent}%
                </Typography>
              )}
            </Stack>
          ) : null;

          return (
            <InvoiceLink invoice={item}>
              <Stack spacing={0.25} alignItems="flex-end">
                <Typography
                  component="strong"
                  sx={{
                    fontWeight: 600,
                    textDecoration: item.status === 'void' ? 'line-through' : 'none',
                  }}>
                  {formatAmount(item?.total, item?.paymentCurrency.decimal)}&nbsp;
                  {item?.paymentCurrency.symbol}
                </Typography>
                {usdAmount && (
                  <Tooltip title={tooltipContent} placement="top" arrow>
                    <Typography
                      variant="caption"
                      sx={{
                        color: 'text.secondary',
                        fontSize: '0.75rem',
                        fontWeight: 400,
                        lineHeight: 1.2,
                      }}>
                      ≈ ${usdAmount}
                    </Typography>
                  </Tooltip>
                )}
              </Stack>
            </InvoiceLink>
          );
        },
      },
    },
    {
      label: t('common.paymentMethod'),
      name: 'paymentMethod',
      width: 120,
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const invoice = data?.list[index] as TInvoiceExpanded;
          return (
            <InvoiceLink invoice={invoice}>
              <Typography sx={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}>
                <Avatar src={invoice.paymentMethod.logo} sx={{ width: 18, height: 18, mr: 1 }} />
                {invoice.paymentMethod.name}
              </Typography>
            </InvoiceLink>
          );
        },
      },
    },
    {
      label: t('common.status'),
      name: 'status',
      width: 120,
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          const isSlippageExceeded = item?.status === 'uncollectible' && item?.metadata?.slippage?.below_threshold;
          return (
            <InvoiceLink invoice={item}>
              <Stack direction="row" spacing={0.5} alignItems="center">
                <Status label={item?.status} color={getInvoiceStatusColor(item?.status)} />
                {isSlippageExceeded && (
                  <Tooltip
                    title={t('payment.customer.invoice.slippageExceededDetail', {
                      currentRate: item.metadata?.slippage?.rate_at_invoice || '—',
                      minRate: item.metadata?.slippage?.min_acceptable_rate || '—',
                    })}
                    arrow
                    placement="top">
                    <WarningAmberIcon sx={{ fontSize: 16, color: 'warning.main', cursor: 'help' }} />
                  </Tooltip>
                )}
              </Stack>
            </InvoiceLink>
          );
        },
      },
    },
    {
      label: t('common.type'),
      name: 'billing_reason',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return (
            <InvoiceLink invoice={item}>
              <Status label={getInvoiceDescriptionAndReason(item, locale)?.type} />
            </InvoiceLink>
          );
        },
      },
    },
    {
      label: t('admin.invoice.number'),
      name: 'number',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return <InvoiceLink invoice={item}>{item.number}</InvoiceLink>;
        },
      },
    },
    {
      label: t('common.description'),
      name: 'description',
      minWidth: 120,
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return (
            <InvoiceLink invoice={item}>
              {getInvoiceDescriptionAndReason(item, locale)?.description || item?.id}
            </InvoiceLink>
          );
        },
      },
    },
    {
      label: t('common.createdAt'),
      name: 'created_at',
      options: {
        sort: true,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return <InvoiceLink invoice={item}>{formatTime(item.created_at)}</InvoiceLink>;
        },
      },
    },
    {
      label: t('common.updatedAt'),
      name: 'updated_at',
      options: {
        sort: true,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return <InvoiceLink invoice={item}>{formatTime(item.updated_at)}</InvoiceLink>;
        },
      },
    },
    {
      label: t('common.actions'),
      name: 'id',
      width: 100,
      align: 'center',
      options: {
        sort: false,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return <InvoiceActions data={item} onChange={refresh} mode={mode} />;
        },
      },
    },
  ].filter(Boolean);

  if (features?.customer) {
    // @ts-ignore
    columns.splice(2, 0, {
      label: t('common.customer'),
      name: 'customer_id',
      options: {
        customBodyRenderLite: (_: string, index: number) => {
          const item = data.list[index] as TInvoiceExpanded;
          return <CustomerLink customer={item.customer} size="small" />;
        },
      },
    });
  }

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

  return (
    <Table
      hasRowLink
      data={data.list}
      durable={`__${listKey}__`}
      durableKeys={['searchText']}
      columns={columns}
      loading={!data.list}
      onChange={onTableChange}
      options={{
        count: data.count,
        page: search!.page - 1,
        rowsPerPage: search!.pageSize,
        onColumnSortChange(_: any, order: any) {
          setSearch({
            ...search!,
            q: search!.q || {},
            o: order,
          });
        },
        onSearchChange: (text: string) => {
          if (text) {
            setSearch({
              ...search!,
              q: {
                'like-description': text,
                'like-metadata': text,
                'like-number': text,
              },
              pageSize: 100,
              page: 1,
            });
          } else {
            setSearch({
              ...search!,
              pageSize: 100,
              page: 1,
              q: {},
            });
          }
        },
      }}
      toolbar={features?.toolbar}
      footer={features?.footer}
      title={
        features?.filter && (
          <FilterToolbar
            setSearch={setSearch}
            search={search}
            status={['open', 'paid', 'uncollectible', 'draft', 'void']}
            currency
          />
        )
      }
      emptyNodeText={`${t('empty.invoices')}`}
    />
  );
}
