import React, { useEffect, useRef, useState } from 'react';
import { Select } from 'antd';
import useDebounce from '@/hooks/useDebounce';
import { getCampaigns } from '../service';
import type { FacebookCampaign } from '../interface';

interface CampaignSelectProps {
  placeholder?: string;
  city?: string;
  value?: string;
  valueLabel?: string;
  disabled?: boolean;
  onSelect?: (campaign: FacebookCampaign | undefined) => void;
  onTotalChange?: (total: number) => void;
  /* Fires once, after the very first page of campaigns has loaded (success or
    failure) — lets the parent show a full-section loader only for that first,
   potentially slow load, not for every later search/pagination fetch. */
  onFirstLoad?: () => void;
}

const CampaignSelect: React.FC<CampaignSelectProps> = ({
  placeholder = 'Select a campaign',
  city,
  value: controlledValue,
  valueLabel,
  disabled,
  onSelect,
  onTotalChange,
  onFirstLoad,
}) => {
  const [campaigns, setCampaigns] = useState<FacebookCampaign[]>([]);
  const [loading, setLoading] = useState(true);
  const [searchQuery, setSearchQuery] = useState('');
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(false);
  const [value, setValue] = useState<string | undefined>(controlledValue);
  const loadMoreLock = useRef(false);
  const firstLoadFired = useRef(false);

  const debouncedSearch = useDebounce(searchQuery, 400);

  const fetchCampaigns = (search: string, nextPage: number): void => {
    setLoading(true);
    // Real backend call — city is not a supported filter (Facebook campaigns
    // aren't tagged with a city), so it's only used locally to reset the
    // selection when the surrounding form's city changes, not sent to the API.
    getCampaigns('facebook', { search, page: nextPage, pageSize: 50 })
      .then((res: any) => {
        const body = res?.data || {};
        const list: FacebookCampaign[] = body.data || [];
        setPage(body.page || nextPage);
        setHasMore(!!body.hasMore);
        onTotalChange?.(body.total ?? list.length);
        setCampaigns((prev) => (nextPage === 1 ? list : [...prev, ...list]));
      })
      .finally(() => {
        setLoading(false);
        loadMoreLock.current = false;
        if (!firstLoadFired.current) {
          firstLoadFired.current = true;
          onFirstLoad?.();
        }
      });
  };

  // A pre-selected campaign (edit mode) may arrive in the same render pass as a
  // `city` change — mark it so the merged effect below doesn't clear it.
  useEffect(() => {
    if (controlledValue !== undefined) setValue(controlledValue);
  }, [controlledValue]);

  // Campaigns aren't city-scoped, so a city change neither refetches the list
  // nor clears the selected campaign — only the search term drives a fetch.
  useEffect(() => {
    fetchCampaigns(debouncedSearch, 1);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [debouncedSearch]);

  const handlePopupScroll = (e: React.UIEvent<HTMLDivElement>): void => {
    const { currentTarget } = e;
    const reachedBottom = currentTarget.scrollTop + currentTarget.clientHeight >= currentTarget.scrollHeight - 5;
    if (reachedBottom && hasMore && !loading && !loadMoreLock.current) {
      loadMoreLock.current = true;
      fetchCampaigns(searchQuery, page + 1);
    }
  };

  const options = campaigns.map((c) => ({ value: c.campaign_id, label: c.name }));
  const mergedOptions =
    value && valueLabel && !campaigns.some((c) => c.campaign_id === value)
      ? [{ value, label: valueLabel }, ...options]
      : options;

  return (
    <Select
      className="block mt-1"
      style={{ width: '100%' }}
      showSearch
      allowClear
      disabled={disabled}
      loading={loading}
      placeholder={placeholder}
      value={value}
      filterOption={false}
      onSearch={setSearchQuery}
      onPopupScroll={handlePopupScroll}
      onChange={(selectedValue) => {
        setValue(selectedValue);
        if (!selectedValue) {
          onSelect?.(undefined);
          return;
        }
        const campaign = campaigns.find((c) => c.campaign_id === selectedValue);
        onSelect?.(campaign);
      }}
      options={mergedOptions}
    />
  );
};

export default CampaignSelect;
