import Taro from '@tarojs/taro';

import { List, PullRefresh } from '@taroify/core';
import classNames from 'classnames';
import { isEqual } from 'lodash-es';
import { useCallback, useEffect, useRef, useState } from 'react';

import AnchorCard from '@/components/anchor-card';
import ListPlaceholder from '@/components/list-placeholder';
import LoginDialog from '@/components/login-dialog';
import { EventName } from '@/constants/app';
import { AnchorSortType } from '@/constants/material';
import useUserInfo from '@/hooks/use-user-info';
import { AnchorInfo, GetAnchorListRequest, IAnchorFilters } from '@/types/material';
import { logWithPrefix } from '@/utils/common';
import { requestAnchorList as requestData } from '@/utils/material';
import { getAgreementSigned, isNeedLogin, setAgreementSigned } from '@/utils/user';

import { AgreementPopup } from '../agreement-popup';

import './index.less';

interface IRequestProps extends Partial<GetAnchorListRequest> {
  filters?: IAnchorFilters;
}

export interface IAnchorListProps extends IRequestProps {
  ready?: boolean;
  refreshDisabled?: boolean;
  listHeight?: number;
  className?: string;
  onListEmpty?: () => void;
}

const FIRST_PAGE = 0;
const PAGE_SIZE = 10;
const PREFIX = 'anchor-list';
const log = logWithPrefix(PREFIX);

function AnchorList(props: IAnchorListProps) {
  const {
    className,
    listHeight,
    refreshDisabled,
    jobId,
    filters,
    cityCode = 'ALL',
    sortType = AnchorSortType.Recommend,
    latitude,
    longitude,
    ready,
    onListEmpty,
  } = props;
  const [refreshing, setRefreshing] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  const [loadingMore, setLoadingMore] = useState(false);
  const [loadMoreError, setLoadMoreError] = useState(false);
  const [dataList, setDataList] = useState<AnchorInfo[]>([]);
  const currentPage = useRef<number>(FIRST_PAGE);
  const requestProps = useRef<IRequestProps>({});
  const prevRequestProps = useRef<IRequestProps>({});
  const onListEmptyRef = useRef(onListEmpty);
  const [openLogin, setLoginOpen] = useState(false);
  const [openAssignment, setAssignmentOpen] = useState(false);
  const successCallback = useRef<() => void>(() => {});
  const userInfo = useUserInfo();
  const needLogin = isNeedLogin(userInfo);

  const handleRefresh = useCallback(async () => {
    log('start pull refresh');
    try {
      setRefreshing(true);
      setLoadMoreError(false);
      const { page, hasMore: more, data: anchorResults } = await requestData({ ...requestProps.current, page: 1 });
      setHasMore(more);
      setDataList(anchorResults);
      currentPage.current = page;
      !anchorResults.length && onListEmptyRef.current?.();
      log('pull refresh success');
    } catch (e) {
      setDataList([]);
      setHasMore(false);
      setLoadMoreError(true);
      currentPage.current = FIRST_PAGE;
      log('pull refresh failed');
    } finally {
      setRefreshing(false);
    }
  }, []);

  const handleLoadMore = useCallback(async () => {
    log('start load more', hasMore);
    if (!hasMore) {
      return;
    }
    setLoadMoreError(false);
    setLoadingMore(true);
    try {
      const {
        page,
        hasMore: more,
        data: anchorResults,
      } = await requestData({ ...requestProps.current, page: currentPage.current + 1 });
      setDataList([...dataList, ...anchorResults]);
      setHasMore(more);
      currentPage.current = page;
      log('load more success');
    } catch (e) {
      setLoadMoreError(true);
      log('load more failed');
    } finally {
      setLoadingMore(false);
    }
  }, [dataList, hasMore]);

  const handleReadMaterial = useCallback(
    (materialId: string) => {
      const index = dataList.findIndex(d => String(d.id) === materialId);
      if (index < 0) {
        return;
      }
      const material = dataList[index];
      if (!material || material.isRead) {
        return;
      }
      log('auto mark read', materialId);
      dataList.splice(index, 1, { ...material, isRead: true });
      setDataList([...dataList]);
    },
    [dataList]
  );

  const handleCancel = useCallback(() => {
    setAssignmentOpen(false);
    setAgreementSigned(false);
  }, []);

  const handleConfirm = useCallback(() => {
    setAssignmentOpen(false);
    setAgreementSigned(true);
    successCallback.current();
  }, []);

  const handleLoginSuccess = useCallback(() => {
    setLoginOpen(false);
    successCallback.current();
  }, []);

  const handleLoginCancel = useCallback(() => {
    setLoginOpen(false);
  }, []);

  const validator = (onSuccess: () => void) => {
    successCallback.current = onSuccess;
    if (!getAgreementSigned()) {
      setAssignmentOpen(true);
    } else if (needLogin) {
      setLoginOpen(true);
    } else {
      onSuccess();
    }
  };

  useEffect(() => {
    onListEmptyRef.current = onListEmpty;
  }, [onListEmpty]);

  useEffect(() => {
    log('request params changed');
    requestProps.current = {
      ...filters,
      jobId,
      cityCode,
      sortType,
      latitude,
      longitude,
      pageSize: PAGE_SIZE,
    };
  }, [jobId, filters, cityCode, sortType, latitude, longitude]);

  useEffect(() => {
    Taro.eventCenter.on(EventName.VIEW_MATERIAL_SUCCESS, handleReadMaterial);
    return () => {
      Taro.eventCenter.off(EventName.VIEW_MATERIAL_SUCCESS, handleReadMaterial);
    };
  }, [handleReadMaterial]);

  // 初始化数据&配置变更后刷新数据
  useEffect(() => {
    // 相比前一次可见时没有数据变更时，不再重新请求
    if (isEqual(prevRequestProps.current, requestProps.current)) {
      log('visible/city changed, but request params not change, ignore');
      return;
    }
    // 列表不可见时，先不做处理
    if (!ready) {
      log('visible/city changed, but is not ready, only refresh list');
      return;
    }

    prevRequestProps.current = requestProps.current;
    const refresh = async () => {
      log('visible/city changed, start refresh list data');
      try {
        setDataList([]);
        setLoadingMore(true);
        setLoadMoreError(false);
        const { page, hasMore: more, data: anchorResults } = await requestData({ ...requestProps.current, page: 1 });
        setHasMore(more);
        setDataList(anchorResults);
        currentPage.current = page;
        !anchorResults.length && onListEmptyRef.current?.();
      } catch (e) {
        setDataList([]);
        setHasMore(false);
        setLoadMoreError(true);
        currentPage.current = FIRST_PAGE;
      } finally {
        log('visible/city changed, refresh list data end');
        setLoadingMore(false);
      }
    };
    refresh();
  }, [jobId, ready, filters, cityCode, sortType]);

  return (
    <>
      <PullRefresh
        className={classNames(`${PREFIX}__pull-refresh`, className)}
        loading={refreshing}
        onRefresh={handleRefresh}
        disabled={refreshDisabled || !ready}
      >
        <List
          hasMore={hasMore}
          onLoad={handleLoadMore}
          loading={loadingMore || refreshing}
          disabled={loadMoreError || !ready}
          fixedHeight={typeof listHeight !== 'undefined'}
          style={listHeight ? { height: `${listHeight}px` } : undefined}
        >
          {dataList.map(item => (
            <AnchorCard data={item} jobId={jobId} key={item.id} validator={validator} />
          ))}
          <ListPlaceholder hasMore={hasMore} loadingMore={loadingMore} loadMoreError={loadMoreError} />
        </List>
      </PullRefresh>
      <AgreementPopup
        open={openAssignment}
        onCancel={handleCancel}
        onConfirm={handleConfirm}
        needBindPhone={needLogin}
      />
      {openLogin && <LoginDialog onCancel={handleLoginCancel} onSuccess={handleLoginSuccess} />}
    </>
  );
}

export default AnchorList;
