/*
 * Copyright 2025 coze-dev Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import React, { useEffect, useState, useRef, useMemo } from 'react';

import { debounce } from 'lodash-es';
import { PUBLIC_SPACE_ID } from '@coze-workflow/base/constants';
import { concatTestId } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { Typography, Spin, Avatar, Select } from '@coze-arch/bot-semi';
import {
  ListBotDraftType,
  PublishStatus,
} from '@coze-arch/bot-api/developer_api';
import { DeveloperApi } from '@coze-arch/bot-api';
import { IconSearch } from '@douyinfe/semi-icons';

import { useGlobalState } from '../../hooks';
import { useExtraBotOption } from './use-extra-bot-option';
import type { IBotSelectOption } from './types';

import styles from './bots.module.less';

type IBotSelectOptions = IBotSelectOption[];

const RenderCustomOption = item => {
  if (!item) {
    return null;
  }
  return (
    <Select.Option
      data-testid={concatTestId(
        'workflow',
        'playground',
        'testrun',
        'bot-select',
        'option',
        item.value,
      )}
      value={item.value}
      showTick={true}
      key={item.value}
      className={styles['bot-option']}
    >
      <div className="flex" style={{ width: '100%' }}>
        <Avatar
          size="extra-extra-small"
          style={{ flexShrink: 0, marginRight: 8 }}
          shape="square"
          src={item.avatar}
        />
        <div className="flex" style={{ flexGrow: 1, flexShrink: 1, width: 0 }}>
          <Typography.Text
            ellipsis={{ showTooltip: true }}
            style={{
              fontSize: 12,
              color: '#1D1C23',
              fontWeight: 400,
            }}
          >
            {item.name}
          </Typography.Text>
        </div>
      </div>
    </Select.Option>
  );
};

const RenderFootLoading = ({
  onObserver,
}: {
  onObserver: () => Promise<void>;
}) => {
  const indicatorRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const callback = entries => {
      if (entries[0].isIntersecting) {
        onObserver?.();
      }
    };
    const loadingObserver = new IntersectionObserver(callback);
    indicatorRef.current && loadingObserver.observe(indicatorRef.current);
    return () => loadingObserver.disconnect();
  }, []);
  return (
    <div className={styles['loading-tag']} ref={indicatorRef}>
      <Spin style={{ marginRight: 10 }} size="small" />
      <span>{I18n.t('workflow_add_common_loading')}</span>
    </div>
  );
};

interface BotsProps {
  value?: string;
  onChange?: (value: string) => void;
}

export const Bots: React.FC<BotsProps> = ({ value, onChange, ...props }) => {
  const globalState = useGlobalState();
  const DebounceTime = 500;

  const isLoadMoreDate = useRef(false);
  const [selectList = [], setSelectList] = useState<IBotSelectOptions>([]);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [isShowFoot, setIsShowFoot] = useState<boolean>(false);
  const [pageIndex, setPageIndex] = useState<number>(1);
  const [search, setSearch] = useState<string>('');
  const [searchTotal, setTotal] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);

  // 由于分页限制 选中的botId可能找不到对应的option 需要额外添加
  const extraBotOption = useExtraBotOption(selectList, value);

  // 接口得到的总数并非真实的总数，前端可能会拼接 options
  const listMaxHeight = useMemo(() => {
    const realTotal = extraBotOption ? searchTotal + 1 : searchTotal;
    // eslint-disable-next-line @typescript-eslint/no-magic-numbers
    return realTotal < 7 ? realTotal * 32 : 208;
  }, [searchTotal, extraBotOption]);

  useEffect(() => {
    fetchBotList();
  }, []);

  const fetchBotList = async (
    index?: number,
    query?: string,
    isReset = false,
  ) => {
    if (query) {
      setIsLoading(true);
    }

    const res = await DeveloperApi.GetDraftBotList({
      space_id:
        globalState.spaceId === PUBLIC_SPACE_ID
          ? globalState.personalSpaceId
          : globalState.spaceId,
      bot_name: query ?? search,
      order_by: 0,
      team_bot_type: ListBotDraftType.TeamBots,
      page_index: index ?? pageIndex,
      page_size: 30,
      is_publish: PublishStatus.All,
    });
    const { bot_draft_list, total = 0 } = res?.data ?? {};
    const list: IBotSelectOptions = (bot_draft_list ?? []).map(it => ({
      name: it.name ?? '',
      value: it.id ?? '',
      avatar: it.icon_url ?? '',
    }));
    const totalList = isReset ? list : [...selectList, ...list];

    setTotal(total);
    setSelectList(totalList);
    setIsShowFoot(totalList.length < total);
    setIsLoading(false);
  };

  const loadMoreData = async () => {
    if (isLoadMoreDate.current) {
      return;
    }
    isLoadMoreDate.current = true;
    const newPageIndex = pageIndex + 1;
    setPageIndex(newPageIndex);
    await fetchBotList(newPageIndex);
    isLoadMoreDate.current = false;
  };

  const handleSearch = query => {
    setSearch(query);
    setPageIndex(1);
    fetchBotList(1, query, true);
  };

  return (
    <div className={styles['select-wrapper']} ref={containerRef}>
      <Select
        value={value}
        data-testid={concatTestId(
          'workflow',
          'playground',
          'testerun',
          'bot-select',
        )}
        dropdownClassName={styles.dropdown}
        showClear
        filter
        remote
        placeholder={I18n.t('workflow_detail_testrun_variable_node_select')}
        emptyContent={I18n.t('agentflow_addbot_select_empty_no_bot')}
        onSearch={debounce(handleSearch, DebounceTime)}
        prefix={<IconSearch />}
        loading={isLoading}
        style={{ width: '100%' }}
        virtualize={{
          height: listMaxHeight,
          width: '100%',
          itemSize: 32,
        }}
        onChange={newValue => onChange?.(newValue as string)}
        {...props}
      >
        {[extraBotOption, ...selectList]
          .filter(item => item)
          .map(item => RenderCustomOption(item))}

        {isShowFoot ? (
          <Select.Option
            value={new Date().getTime()}
            key={new Date().getTime()}
            className={styles['bot-foot-loading']}
            disabled
          >
            <RenderFootLoading onObserver={loadMoreData} />
          </Select.Option>
        ) : null}
      </Select>
    </div>
  );
};
