/* eslint-disable react/no-unstable-nested-components */
import CodeBlock from '@arcblock/ux/lib/CodeBlock';
import Toast from '@arcblock/ux/lib/Toast';
import { api, formatTime, ConfirmDialog } from '@blocklet/payment-react';
import type { Paginated, TEvent, TWebhookAttemptExpanded } from '@blocklet/payment-types';
import { CheckCircleOutlined, ErrorOutlined, RefreshOutlined } from '@mui/icons-material';
import {
  Box,
  Button,
  CircularProgress,
  Grid,
  List,
  ListItem,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  ListSubheader,
  Stack,
  Typography,
} from '@mui/material';
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
import { useInfiniteScroll, useRequest, useSetState } from 'ahooks';
import React, { useEffect, useState } from 'react';

import { isEmpty } from 'lodash';
import { isSuccessAttempt } from '../../libs/util';
import RequestInfoPopper, { RequestType } from './request-info';

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

const groupAttemptsByDate = (attempts: TWebhookAttemptExpanded[]) => {
  const groupedAttempts: { [key: string]: TWebhookAttemptExpanded[] } = {};
  attempts.forEach((attempt) => {
    const date = new Date(attempt.created_at).toLocaleDateString();
    if (!groupedAttempts[date]) {
      groupedAttempts[date] = [];
    }
    groupedAttempts[date]?.push(attempt);
  });
  return groupedAttempts;
};

type Props = {
  event_id?: string;
  webhook_endpoint_id?: string;
  event?: TEvent & { requestInfo?: RequestInfo };
};

export default function WebhookAttempts({ event_id = '', webhook_endpoint_id = '', event = undefined }: Props) {
  const { t } = useLocaleContext();
  const { data, loadMore, loadingMore, loading, reload } = useInfiniteScroll<Paginated<TWebhookAttemptExpanded>>(
    (d) => {
      const size = 15;
      const page = d ? Math.ceil(d.list.length / size) + 1 : 1;
      return fetchData({ page, size, event_id, webhook_endpoint_id });
    },
    {
      reloadDeps: [event_id, webhook_endpoint_id],
    }
  );

  const attempts = data?.list || [];

  // @ts-ignore
  const [selected, setSelected] = useState<
    (TWebhookAttemptExpanded & { event: TEvent & { requestInfo?: RequestInfo } }) | null
  >(null);

  const [state, setState] = useSetState({
    retryEventId: '',
  });

  const groupedAttempts = groupAttemptsByDate(attempts);

  const { loading: retrying, run: retryWebhook } = useRequest<any, any[]>(
    (eventId: string) => api.post(`/api/events/${eventId}/retry-webhooks`).then((res: any) => res.data),
    {
      manual: true,
      onSuccess: (result: any) => {
        Toast.success(result.message || t('admin.event.retryOptions.success'));
        reload();
      },
      onError: (err: any) => {
        Toast.error(err.response?.data?.error || t('admin.event.retryOptions.error'));
      },
    }
  );

  useEffect(() => {
    if (!selected && data?.list.length) {
      setSelected(data.list[0] as TWebhookAttemptExpanded);
    }
  }, [data?.list.length, selected]); // eslint-disable-line

  const handleAttemptClick = (attempt: TWebhookAttemptExpanded) => {
    setSelected(attempt);
  };

  const handleRetryClick = (eventId: string) => {
    setState({ retryEventId: eventId });
  };

  const handleConfirmRetry = () => {
    if (state.retryEventId) {
      retryWebhook(state.retryEventId);
      setState({ retryEventId: '' });
    }
  };

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

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

  return (
    <Grid container spacing={2.5}>
      <Grid size={{ xs: 12, md: 4 }} className="attempt-list">
        {isEmpty(Object.keys(groupedAttempts)) ? (
          <Typography
            sx={{
              color: 'text.secondary',
            }}>
            {t('admin.event.noAttempts')}
          </Typography>
        ) : (
          <>
            <List sx={{ maxHeight: '64vh', overflowY: 'scroll' }}>
              {Object.entries(groupedAttempts).map(([date, items]) => (
                <React.Fragment key={date}>
                  <ListSubheader>{date}</ListSubheader>
                  {items.map((attempt) => (
                    <ListItem
                      key={attempt.id}
                      component={ListItemButton}
                      onClick={() => handleAttemptClick(attempt)}
                      selected={selected === attempt}
                      secondaryAction={
                        <Typography
                          sx={{
                            color: 'text.secondary',
                          }}>
                          {formatTime(attempt.created_at, 'HH:mm:ss A')}
                        </Typography>
                      }>
                      <ListItemIcon>
                        {isSuccessAttempt(attempt.response_status) === false ? (
                          <ErrorOutlined color="error" />
                        ) : (
                          <CheckCircleOutlined color="success" />
                        )}
                      </ListItemIcon>
                      <ListItemText
                        primary={
                          <Typography
                            sx={{
                              color: 'text.primary',
                            }}>
                            {attempt.event.type}
                          </Typography>
                        }
                      />
                    </ListItem>
                  ))}
                </React.Fragment>
              ))}
            </List>
            {hasMore && (
              <Button variant="text" type="button" color="inherit" onClick={loadMore} disabled={loadingMore}>
                {loadingMore ? t('common.loading') : t('common.loadMore')}
              </Button>
            )}
            {!hasMore && (
              <Typography
                sx={{
                  color: 'text.secondary',
                }}>
                {t('common.noMoreData')}
              </Typography>
            )}
          </>
        )}
      </Grid>
      <Grid
        size={{
          xs: 12,
          md: 8,
        }}>
        {selected && (
          <Stack direction="column" spacing={2} sx={{ pt: 3, pl: 3, borderLeft: '1px solid', borderColor: 'divider' }}>
            <Stack direction="row" sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
              <Typography variant="h6">{event_id ? selected.endpoint.url : selected.event.type}</Typography>
              <Button
                variant="outlined"
                size="small"
                startIcon={<RefreshOutlined />}
                onClick={() => handleRetryClick(selected.event_id)}
                disabled={retrying}>
                {retrying ? t('admin.event.retrying') : t('admin.event.retry')}
              </Button>
            </Stack>
            {state.retryEventId && (
              <ConfirmDialog
                onConfirm={handleConfirmRetry}
                onCancel={() => setState({ retryEventId: '' })}
                title={t('admin.event.retryOptions.title')}
                message={t('admin.event.retryOptions.confirmMessage')}
                loading={retrying}
                color="primary"
              />
            )}
            <Box>
              <Typography variant="h6">
                {t('admin.event.response')} ({selected.response_status})
              </Typography>
              {/* @ts-ignore */}
              <CodeBlock language="json">{JSON.stringify(selected.response_body, null, 2)}</CodeBlock>
            </Box>
            <Box>
              <Stack
                direction="row"
                spacing={1}
                sx={{
                  alignItems: 'center',
                }}>
                <Typography variant="h6">{t('admin.event.request')}</Typography>
                <RequestInfoPopper
                  // @ts-ignore
                  requestInfo={selected?.event?.requestInfo}
                  request={selected?.event.request as RequestType}
                />
              </Stack>
              {/* @ts-ignore */}
              <CodeBlock language="json">{JSON.stringify(selected.event, null, 2)}</CodeBlock>
            </Box>
          </Stack>
        )}
        {data?.list.length === 0 && event && (
          <Box>
            <Stack
              direction="row"
              spacing={1}
              sx={{
                alignItems: 'center',
              }}>
              <Typography variant="h6">{t('admin.event.eventData')}</Typography>
              <RequestInfoPopper
                // @ts-ignore
                requestInfo={event.requestInfo}
                request={event.request as RequestType}
              />
            </Stack>
            {/* @ts-ignore */}
            <CodeBlock language="json">{JSON.stringify(event.data, null, 2)}</CodeBlock>
          </Box>
        )}
      </Grid>
    </Grid>
  );
}
