
import { useT } from '../../hooks/useT';
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useAsukaStore } from '../../store/asukaStore';
import { DEFAULT_PERMISSIONS } from '../../core/permissions';
import { registerModule, type AsukaModuleComponent } from '../../core/modules';
import { getPixabayApiKey } from '../../core/integrations';
import { useDebouncedValue } from '../../core/hooks/useDebouncedValue';
import { useInfiniteScroll } from '../../core/hooks/useInfiniteScroll';
import { mergeUniqueByKey } from '../../core/search/paged';
import { resolveRemoteImageUrlWithUpload } from '../../core/images';

import { Card, CardHeader, CardTitle, CardContent } from '../core/Card';
import { Field } from '../core/Field';
import { Input } from '../core/Input';
import { Button } from '../core/Button';
import { ImageGrid, type ImageGridItem } from '../core/ImageGrid';
import { ScrollArea } from '../core/ScrollArea';

type PixabayHit = {
  id: number;
  previewURL: string;
  webformatURL: string;
  largeImageURL: string;
  tags: string;
};

const PER_PAGE = 24;
const DEBOUNCE_MS = 350;

function pickPixabayAddSource(hit: PixabayHit): string {
  return hit.webformatURL || hit.largeImageURL || hit.previewURL || '';
}

export const PixabayPanel: AsukaModuleComponent = ({ context }) => {
  const t = useT();
  const permissions = useAsukaStore((s: any) => s.permissions ?? DEFAULT_PERMISSIONS);
  const [query, setQuery] = useState('');
  const debouncedQuery = useDebouncedValue(query, DEBOUNCE_MS);

  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [loadingMore, setLoadingMore] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [hits, setHits] = useState<PixabayHit[]>([]);
  const [hasMore, setHasMore] = useState(true);

  const abortRef = useRef<AbortController | null>(null);
  const requestIdRef = useRef(0);
  const inFlightRef = useRef(false);
  const [scrollEl, setScrollEl] = useState<HTMLDivElement | null>(null);

  const key = useMemo(() => getPixabayApiKey(), []);

  const buildParams = useCallback((q: string, pageNum: number) => {
    const base: Record<string, string> = {
      key: key ?? '',
      image_type: 'photo',
      per_page: String(PER_PAGE),
      page: String(pageNum),
      safesearch: 'true',
    };

    const trimmed = (q ?? '').trim();

    // Default feed: no query => popular/editor's choice
    if (!trimmed) {
      base.order = 'popular';
      base.editors_choice = 'true';
      return base;
    }

    base.q = trimmed;
    return base;
  }, [key]);

  const fetchPage = useCallback(async (q: string, pageNum: number, mode: 'reset' | 'more') => {
    if (!key) return;
    if (mode === 'more' && inFlightRef.current) return;

    if (mode === 'reset') {
      abortRef.current?.abort();
    }

    const requestId = requestIdRef.current + 1;
    requestIdRef.current = requestId;
    inFlightRef.current = true;

    const ac = new AbortController();
    abortRef.current = ac;

    if (mode === 'reset') setLoading(true);
    else setLoadingMore(true);

    setError(null);

    try {
      const url = 'https://pixabay.com/api/?' + new URLSearchParams(buildParams(q, pageNum)).toString();
      const res = await fetch(url, { signal: ac.signal });
      if (!res.ok) throw new Error(`Pixabay error: ${res.status}`);
      const json = await res.json();

      if (ac.signal.aborted || requestId !== requestIdRef.current) return;

      const newHits = ((json?.hits ?? []) as PixabayHit[]) || [];
      const total = Number(json?.totalHits ?? 0);

      if (mode === 'reset') {
        setHits(newHits);
      } else {
        setHits((prev) => mergeUniqueByKey(prev, newHits, (h) => String(h.id)));
      }

      const inferredHasMore = pageNum * PER_PAGE < total && newHits.length > 0;
      setHasMore(inferredHasMore);
    } catch (e: any) {
      if (e?.name === 'AbortError') return;
      if (requestId !== requestIdRef.current) return;
      setError(e?.message ?? 'Request failed');
      if (mode === 'more') setHasMore(false);
    } finally {
      if (requestId === requestIdRef.current) {
        inFlightRef.current = false;
        setLoading(false);
        setLoadingMore(false);
      }
    }
  }, [buildParams, key]);

  // Auto-search on typing (debounced). Also ensures default results when empty.
  useEffect(() => {
    if (!key) return;
    setPage(1);
    setHasMore(true);
    fetchPage(debouncedQuery, 1, 'reset');
  }, [debouncedQuery, fetchPage, key]);

  useEffect(() => {
    return () => {
      abortRef.current?.abort();
      abortRef.current = null;
      inFlightRef.current = false;
    };
  }, []);

  const loadMore = useCallback(async () => {
    if (!key) return;
    if (loading || loadingMore || inFlightRef.current) return;
    if (!hasMore) return;

    const nextPage = page + 1;
    setPage(nextPage);
    await fetchPage(debouncedQuery, nextPage, 'more');
  }, [debouncedQuery, fetchPage, hasMore, key, loading, loadingMore, page]);

  useInfiniteScroll(scrollEl, loadMore, {
    enabled: Boolean(key) && hasMore && !loading && !loadingMore && hits.length > 0,
    thresholdPx: 220,
  });

  const gridItems = useMemo<ImageGridItem[]>(
    () => hits.map((h) => ({
      id: String(h.id),
      src: h.previewURL,
      alt: h.tags,
      badge: 'Add',
    })),
    [hits],
  );

  const addImageToCanvas = useCallback(async (hit: PixabayHit) => {
    if (!((permissions as any)?.canAddImage ?? true) || !((permissions as any)?.canUseExternalImageLibraries ?? true) || !((permissions as any)?.canUsePixabayImages ?? true)) return;
    const engine = context.getEngine?.() ?? null;
    const src = pickPixabayAddSource(hit);
    if (!src || !engine) return;
    try {
      const url = await resolveRemoteImageUrlWithUpload(src, { source: 'pixabay', name: `pixabay-${hit.id}` });
      await engine.objects.addImage({ url, center: true, metadata: { sourceModule: 'pixabay' } } as any);
    } catch {}
  }, [context, permissions]);

  return (
    <Card>
      <CardHeader className="ask-flex ask-items-center ask-justify-between ask-gap-2">
        <CardTitle>{t('ui.pixabay')}</CardTitle>
        <span className="ask-text-[10px] ask-text-textMuted">{t('ui.photos')}</span>
      </CardHeader>

      <CardContent className="ask-flex ask-flex-col ask-gap-4">
        {!key ? (
          <div className="ask-rounded-[14px] ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-3">
            <div className="ask-text-[11px] ask-font-medium">{t('ui.missing_pixabay_api_key')}</div>
            <div className="ask-mt-1 ask-text-[10px] ask-text-textMuted">{t('ui.pixabay_api_key_path')}</div>
          </div>
        ) : (
          <>
            <Field label={t('common.search')}>
              <div className="ask-flex ask-items-center ask-gap-2">
                <Input
                  value={query}
                  onInput={(e) => setQuery((e.target as HTMLInputElement).value)}
                  placeholder={t('common.search')}
                  disabled={false}
                />
                <Button
                  size="sm"
                  onClick={() => {
                    setPage(1);
                    setHasMore(true);
                    void fetchPage(query, 1, 'reset');
                  }}
                  disabled={loading}
                  title={t('ui.search_now')}
                >
                  {loading ? t('common.loading') : t('common.search')}
                </Button>
              </div>
              <div className="ask-mt-1 ask-text-[10px] ask-text-textMuted">{t('ui.results_update_automatically_while_you_type_debounced')}</div>
            </Field>

            {error ? <div className="ask-text-[11px] ask-text-red-600">{error}</div> : null}

            <ScrollArea
              className="ask-h-[420px] ask-max-h-[min(60vh,420px)] ask-min-h-[240px]"
              viewportClassName="ask-h-full ask-pr-1"
              viewportRef={setScrollEl}
            >
              {gridItems.length > 0 ? (
                <ImageGrid
                  items={gridItems}
                  columns={2}
                  onClick={(it) => {
                    const hit = hits.find((h) => String(h.id) === it.id);
                    if (hit) addImageToCanvas(hit);
                  }}
                />
              ) : null}

              {loadingMore ? (
                <div className="ask-mt-3 ask-text-center ask-text-[10px] ask-text-textMuted">{t('common.loadingMore')}</div>
              ) : null}

              {!loading && hits.length === 0 && !error ? (
                <div className="ask-mt-3 ask-text-center ask-text-[10px] ask-text-textMuted">{t('ui.no_results_try_another_query')}</div>
              ) : null}
            </ScrollArea>

            <div className="ask-text-[10px] ask-text-textMuted">{t('ui.scroll_to_the_bottom_to_load_more_click_an_image_to_add_it_to_the_canvas')}</div>
          </>
        )}
      </CardContent>
    </Card>
  );
};

registerModule({
  id: 'image-pixabay',
  label: 'Pixabay',
  icon: 'pixabay',
  displayMode: 'floating',
  keepMounted: false,
  licenseFeature: 'pixabay',
  supportedEngines: ['fabric', '3d'],
  requiredCapabilities: ['addImage'],
  isAvailable: Boolean(getPixabayApiKey()),
  render: PixabayPanel,
});

export default PixabayPanel;
