/* eslint-disable react/no-unstable-nested-components */
import { getDurableData } from '@arcblock/ux/lib/Datatable';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import { api, formatTime, Table, formatBNStr, getCustomerAvatar } from '@blocklet/payment-react';
import type { TCustomer, TMeter, TMeterEventExpanded } from '@blocklet/payment-types';
import {
  CircularProgress,
  Chip,
  Box,
  Button,
  Menu,
  MenuItem,
  TextField,
  styled,
  Checkbox,
  ListItemText,
} from '@mui/material';
import { useEffect, useMemo, useState } from 'react';
import { Link, useSearchParams } from 'react-router-dom';
import { Add, Close } from '@mui/icons-material';

import { useRequest } from 'ahooks';
import CustomerLink from '../../../../components/customer/link';
import InfoCard from '../../../../components/info-card';

const fetchData = (params: Record<string, any> = {}): Promise<{ list: TMeterEventExpanded[]; count: number }> => {
  const search = new URLSearchParams();
  Object.keys(params).forEach((key) => {
    const v = params[key];
    if (v !== undefined && v !== null && v !== '') {
      if (key === 'q') {
        const queryStr = Object.entries(v)
          .map((x) => x.join(':'))
          .join(' ');
        search.set(key, queryStr);
      } else {
        search.set(key, String(v));
      }
    }
  });
  return api.get(`/api/meter-events?${search.toString()}`).then((res: any) => res.data);
};

const fetchCustomers = (params: Record<string, any> = {}): Promise<{ list: TCustomer[]; count: number }> => {
  const search = new URLSearchParams();
  Object.keys(params).forEach((key) => {
    let v = params[key];
    if (key === 'q') {
      v = Object.entries(v)
        .map((x) => x.join(':'))
        .join(' ');
    }
    search.set(key, String(v));
  });
  return api.get(`/api/customers?${search.toString()}`).then((res: any) => res.data);
};

const fetchMeters = (): Promise<{ list: TMeter[]; count: number }> => {
  return api.get('/api/meters?pageSize=100').then((res: any) => res.data);
};

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

const parseSearchParams = (searchParamsString: string) => {
  if (!searchParamsString) {
    return {
      customer_id: '',
      status: '',
      meter_id: '',
    };
  }

  const params = new URLSearchParams(searchParamsString);
  return {
    customer_id: params.get('customer_id') || '',
    status: params.get('status') || '',
    meter_id: params.get('meter_id') || '',
  };
};

export default function MeterEventsList() {
  const listKey = 'meter-events';
  const persisted = getDurableData(listKey);

  const { t } = useLocaleContext();
  const [searchParams] = useSearchParams();
  const searchParamsString = searchParams.toString();
  const [search, setSearch] = useState<SearchProps>(() => {
    const initialFilters = parseSearchParams(searchParamsString);
    return {
      status: initialFilters.status,
      customer_id: initialFilters.customer_id,
      meter_id: initialFilters.meter_id,
      pageSize: persisted.rowsPerPage || 20,
      page: persisted.page ? persisted.page + 1 : 1,
    };
  });

  const [data, setData] = useState<{ list: TMeterEventExpanded[]; count: number } | null>(null);
  const [loading, setLoading] = useState(true);
  const [meters, setMeters] = useState<TMeter[]>([]);

  useEffect(() => {
    const { customer_id: customerId, status, meter_id: meterId } = parseSearchParams(searchParamsString);

    if (!customerId && !status && !meterId) return;

    setSearch((prev: any) => ({
      ...prev,
      customer_id: customerId,
      status,
      meter_id: meterId,
      page: 1,
    }));
  }, [searchParamsString]);

  useEffect(() => {
    setLoading(true);
    fetchData(search)
      .then((res) => setData(res))
      .finally(() => setLoading(false));
  }, [search]);

  useEffect(() => {
    fetchMeters()
      .then((res) => setMeters(res.list))
      .catch(() => setMeters([]));
  }, []);

  const meterByEventName = useMemo(() => {
    return new Map(meters.map((meter) => [meter.event_name, meter]));
  }, [meters]);

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

  const getStatusColor = (status: string) => {
    switch (status) {
      case 'pending':
        return 'info';
      case 'requires_action':
        return 'warning';
      case 'requires_capture':
        return 'warning';
      case 'completed':
        return 'success';
      case 'processing':
        return 'info';
      case 'canceled':
        return 'default';
      default:
        return 'default';
    }
  };

  const handleSearchChange = (updates: Partial<SearchProps>) => {
    setSearch((prev: any) => ({
      ...prev,
      ...updates,
      page: 1,
    }));
  };

  const columns = [
    {
      label: t('common.customer'),
      name: 'customer',
      options: {
        filter: false,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data?.list[index] as TMeterEventExpanded;
          return item.customer ? (
            <Link to={`/admin/customers/${item.customer.id}`}>
              <CustomerLink customer={item.customer} size="small" />
            </Link>
          ) : (
            '-'
          );
        },
      },
    },
    {
      label: t('common.meter'),
      name: 'event_name',
      options: {
        filter: false,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data?.list[index] as TMeterEventExpanded | undefined;
          if (!item) {
            return '-';
          }

          const meter = item.meter || meterByEventName.get(item.event_name);
          if (!meter) {
            return item.event_name || '-';
          }

          return <Link to={`/admin/billing/${meter.id}`}>{item.event_name}</Link>;
        },
      },
    },
    {
      label: t('admin.meterEvent.settlementAmount'),
      name: 'settlement_amount',
      options: {
        filter: false,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data?.list[index] as TMeterEventExpanded;
          // payload.decimal 是存储在数据中的 decimal 值，更准确
          const reportedDecimal = (item.payload as any)?.decimal ?? item.paymentCurrency?.decimal ?? 18;
          const reportedValue = item.payload?.value;
          const consumedValue = item.credit_consumed;
          const hasReported = reportedValue !== undefined && reportedValue !== null && reportedValue !== '';
          const hasConsumed = consumedValue !== undefined && consumedValue !== null && consumedValue !== '';

          if (!hasReported && !hasConsumed) {
            return <Link to={`/admin/billing/${item.id}`}>-</Link>;
          }

          const formattedReported = hasReported ? formatBNStr(reportedValue, reportedDecimal) : '-';
          const consumedDecimal = item.paymentCurrency?.decimal ?? reportedDecimal;
          const formattedConsumed = hasConsumed ? formatBNStr(consumedValue, consumedDecimal) : '-';
          const symbol = item.paymentCurrency?.symbol || '';
          const isUnsettled = Boolean(item.credit_pending && item.credit_pending !== '0');

          return (
            <Link to={`/admin/billing/${item.id}`}>
              <Box
                component="span"
                sx={{
                  color: isUnsettled ? 'warning.main' : 'text.primary',
                  fontWeight: isUnsettled ? 600 : 400,
                }}>
                {formattedConsumed} / {formattedReported}
                {symbol ? ` ${symbol}` : ''}
              </Box>
            </Link>
          );
        },
      },
    },
    {
      label: t('common.status'),
      name: 'status',
      options: {
        filter: false,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data?.list[index] as TMeterEventExpanded;
          return (
            <Link to={`/admin/billing/${item.id}`}>
              <Chip
                label={item.status}
                color={getStatusColor(item.status)}
                size="small"
                sx={{ textTransform: 'capitalize' }}
              />
            </Link>
          );
        },
      },
    },
    {
      label: t('admin.meterEvent.reportedAt'),
      name: 'timestamp',
      options: {
        sort: true,
        customBodyRenderLite: (_: string, index: number) => {
          const item = data?.list[index] as TMeterEventExpanded;
          return <Link to={`/admin/billing/${item.id}`}>{formatTime(item.timestamp * 1000)}</Link>;
        },
      },
    },
  ];

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

  return (
    <Table
      hasRowLink
      durable={`__${listKey}__`}
      durableKeys={['page', 'rowsPerPage', 'searchText']}
      data={data?.list || []}
      columns={columns}
      loading={loading}
      onChange={onTableChange}
      options={{
        count: data?.count || 0,
        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-event_name': text,
                'like-id': text,
              },
              pageSize: 100,
              page: 1,
            });
          } else {
            setSearch({
              ...search!,
              pageSize: 100,
              page: 1,
              q: {},
            });
          }
        },
      }}
      title={<FilterToolbar search={search} onSearchChange={handleSearchChange} meters={meters} />}
    />
  );
}

function FilterToolbar({
  search,
  onSearchChange,
  meters,
}: {
  search: SearchProps;
  onSearchChange: (updates: Partial<SearchProps>) => void;
  meters: TMeter[];
}) {
  return (
    <Root>
      <Box className="table-toolbar-left">
        <StatusFilter search={search} onSearchChange={onSearchChange} />
        <CustomerFilter search={search} onSearchChange={onSearchChange} />
        <MeterFilter search={search} onSearchChange={onSearchChange} meters={meters} />
      </Box>
    </Root>
  );
}

function StatusFilter({
  search,
  onSearchChange,
}: {
  search: SearchProps;
  onSearchChange: (updates: Partial<SearchProps>) => void;
}) {
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
  const { t } = useLocaleContext();
  const statuses = ['pending', 'processing', 'requires_action', 'requires_capture', 'completed', 'canceled'];
  const selectedStatuses = search.status ? search.status.split(',').filter(Boolean) : [];

  const handleToggleStatus = (status: string) => {
    const newStatuses = selectedStatuses.includes(status)
      ? selectedStatuses.filter((s) => s !== status)
      : [...selectedStatuses, status];

    onSearchChange({ status: newStatuses.join(',') });
  };

  return (
    <section>
      <Button className="option-btn" variant="text" onClick={(e) => setAnchorEl(e.currentTarget)}>
        {search.status ? (
          <Close
            sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.05rem' }}
            onClick={(e) => {
              e.stopPropagation();
              onSearchChange({ status: '' });
              setAnchorEl(null);
            }}
          />
        ) : (
          <Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.05rem' }} />
        )}
        {t('common.status')}
        <span>{selectedStatuses.length > 0 ? `${selectedStatuses.length} selected` : ''}</span>
      </Button>
      <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)} className="status-options">
        {statuses.map((status) => (
          <MenuItem key={status} onClick={() => handleToggleStatus(status)} sx={{ py: 0.5 }}>
            <Checkbox checked={selectedStatuses.includes(status)} size="small" />
            <ListItemText primary={status} />
          </MenuItem>
        ))}
      </Menu>
    </section>
  );
}

function CustomerFilter({
  search,
  onSearchChange,
}: {
  search: SearchProps;
  onSearchChange: (updates: Partial<SearchProps>) => void;
}) {
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
  const [customers, setCustomers] = useState<TCustomer[]>([]);
  const [searchText, setSearchText] = useState('');
  const { t } = useLocaleContext();

  const customerRequest = useRequest(
    (text: string) => {
      return fetchCustomers({
        q: {
          'like-name': text,
          'like-email': text,
          'like-did': text,
        },
        page: 1,
        pageSize: 10,
      });
    },
    {
      onSuccess: (data) => {
        setCustomers(data.list);
      },
      debounceWait: 500,
    }
  );

  useEffect(() => {
    customerRequest.run(searchText);
  }, [searchText]);

  const selectedCustomer = customers.find((c) => c.id === search.customer_id);

  return (
    <section>
      <Button className="option-btn" variant="text" onClick={(e) => setAnchorEl(e.currentTarget)}>
        {search.customer_id ? (
          <Close
            sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.05rem' }}
            onClick={(e) => {
              e.stopPropagation();
              onSearchChange({ customer_id: '' });
              setAnchorEl(null);
            }}
          />
        ) : (
          <Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.05rem' }} />
        )}
        {t('common.customer')}
        <span>{selectedCustomer?.name || ''}</span>
      </Button>
      <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)}>
        <Box sx={{ p: 1 }}>
          <TextField
            placeholder="Search customer"
            size="small"
            sx={{ width: '100%' }}
            value={searchText}
            onChange={(e) => {
              setSearchText(e.target.value);
              e.stopPropagation();
            }}
            onClick={(e) => e.stopPropagation()}
          />
        </Box>
        {customers.map((customer) => (
          <MenuItem
            key={customer.id}
            onClick={(e) => {
              e.stopPropagation();
              onSearchChange({ customer_id: customer.id });
              setAnchorEl(null);
            }}>
            <InfoCard
              logo={getCustomerAvatar(
                customer?.did,
                customer?.updated_at ? new Date(customer.updated_at).toISOString() : undefined,
                48
              )}
              name={customer.name || customer.email}
              key={customer.id}
              description={`${customer.did.slice(0, 6)}...${customer.did.slice(-6)}`}
            />
          </MenuItem>
        ))}
      </Menu>
    </section>
  );
}

function MeterFilter({
  search,
  onSearchChange,
  meters,
}: {
  search: SearchProps;
  onSearchChange: (updates: Partial<SearchProps>) => void;
  meters: TMeter[];
}) {
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
  const { t } = useLocaleContext();

  const selectedMeter = meters.find((m) => m.id === search.meter_id);

  return (
    <section>
      <Button className="option-btn" variant="text" onClick={(e) => setAnchorEl(e.currentTarget)}>
        {search.meter_id ? (
          <Close
            sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.05rem' }}
            onClick={(e) => {
              e.stopPropagation();
              onSearchChange({ meter_id: '' });
              setAnchorEl(null);
            }}
          />
        ) : (
          <Add sx={{ color: 'text.secondary', cursor: 'pointer', fontSize: '1.05rem' }} />
        )}
        {t('admin.meters')}
        <span>{selectedMeter?.name || ''}</span>
      </Button>
      <Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={() => setAnchorEl(null)} className="status-options">
        {meters.map((meter) => (
          <MenuItem
            key={meter.id}
            onClick={() => {
              onSearchChange({ meter_id: meter.id });
              setAnchorEl(null);
            }}>
            {meter.name} ({meter.event_name})
          </MenuItem>
        ))}
      </Menu>
    </section>
  );
}

const Root = styled(Box)`
  .table-toolbar-left {
    display: flex;
    align-items: center;
  }

  .table-toolbar-left section {
    position: relative;
    list-style: none;
    font-size: 14px;
    font-weight: normal;
    padding: 0 5px;
    cursor: pointer;
  }

  .option-btn {
    display: flex;
    align-items: center;
    border-radius: 25px;
    background: ${({ theme }) => theme.palette.grey[100]};
    padding: 5px 10px;
    color: ${({ theme }) => theme.palette.text.secondary};
    font-size: 14px;
    line-height: 14px;
    overflow: visible;
  }

  .option-btn span {
    color: #3773f2;
    padding: 0 3px;
    overflow: visible;
  }

  .status-options {
    max-height: 300px;
    overflow-y: auto;
  }
`;
