
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 { getPexelsApiKey } 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 PexelsPhoto = {
  id: number;
  url?: string;
  photographer?: string;
  photographer_url?: string;
  src: {
    original?: string;
    large2x?: string;
    large?: string;
    medium?: string;
    small?: string;
    portrait?: string;
    landscape?: string;
    tiny?: string;
  };
};

type PexelsResponse = {
  page: number;
  per_page: number;
  photos: PexelsPhoto[];
  total_results?: number;
  next_page?: string;
};

const PER_PAGE = 24;
const DEBOUNCE_MS = 350;

function pickBestSrc(p: PexelsPhoto): string {
  return (
    p.src?.large2x ||
    p.src?.large ||
    p.src?.original ||
    p.src?.medium ||
    p.src?.landscape ||
    p.src?.portrait ||
    p.src?.small ||
    p.src?.tiny ||
    ''
  );
}

export const PexelsPanel: AsukaModuleComponent = ({ context }) => {
  const t = useT();
  const permissions = useAsukaStore((s: any) => s.permissions ?? DEFAULT_PERMISSIONS);
  const key = useMemo(() => getPexelsApiKey(), []);

  const [query, setQuery] = useState('');
  const debouncedQuery = useDebouncedValue(query, DEBOUNCE_MS);

  const [loading, setLoading] = useState(false);
  const [loadingMore, setLoadingMore] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [items, setItems] = useState<PexelsPhoto[]>([]);
  const [scrollEl, setScrollEl] = useState<HTMLDivElement | null>(null);

  const abortRef = useRef<AbortController | null>(null);
  const requestIdRef = useRef(0);
  const inFlightRef = useRef(false);

  const endpoint = useMemo(() => {
    const q = (debouncedQuery ?? '').trim();
    return q ? 'https://api.pexels.com/v1/search' : 'https://api.pexels.com/v1/curated';
  }, [debouncedQuery]);

  const fetchPage = useCallback(async (q: string, pageNumber: 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;

    const isMore = mode === 'more';
    setError(null);
    if (isMore) setLoadingMore(true);
    else setLoading(true);

    try {
      const params = new URLSearchParams();
      params.set('page', String(pageNumber));
      params.set('per_page', String(PER_PAGE));

      const trimmed = (q ?? '').trim();
      if (trimmed) params.set('query', trimmed);

      const res = await fetch(`${endpoint}?${params.toString()}`, {
        signal: ac.signal,
        headers: {
          Authorization: key,
          Accept: 'application/json',
        },
      });

      if (!res.ok) {
        const body = await res.text().catch(() => '');
        throw new Error(`Pexels API error (${res.status})${body ? `: ${body}` : ''}`);
      }

      const data = (await res.json()) as PexelsResponse;
      if (ac.signal.aborted || requestId !== requestIdRef.current) return;

      const photos = Array.isArray(data?.photos) ? data.photos : [];

      setItems((prev) => {
        if (!isMore) return photos;
        return mergeUniqueByKey(prev, photos, (p) => String(p.id));
      });

      const more = Boolean(data?.next_page) || photos.length >= PER_PAGE;
      setHasMore(more);
    } catch (e: any) {
      if (e?.name === 'AbortError') return;
      if (requestId !== requestIdRef.current) return;
      setError(e?.message ?? 'Failed to load Pexels photos.');
      setHasMore(false);
    } finally {
      if (requestId === requestIdRef.current) {
        inFlightRef.current = false;
        if (isMore) setLoadingMore(false);
        else setLoading(false);
      }
    }
  }, [endpoint, key]);

  useEffect(() => {
    if (!key) return;
    setPage(1);
    setHasMore(true);
    void 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 && items.length > 0,
    thresholdPx: 220,
  });

  const gridItems = useMemo<ImageGridItem[]>(
    () => items
      .map((p) => {
        const src = pickBestSrc(p);
        if (!src) return null;
        return {
          id: String(p.id),
          src,
          alt: p.photographer ? `Photo by ${p.photographer}` : 'Pexels photo',
          badge: p.photographer ?? undefined,
        };
      })
      .filter(Boolean) as ImageGridItem[],
    [items],
  );

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

  return (
    <Card>
      <CardHeader className="ask-flex ask-items-center ask-justify-between ask-gap-2">
        <CardTitle>{t('ui.pexels')}</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_pexels_api_key_pass_it_at_init')}</div>
            <div className="ask-mt-1 ask-text-[10px] ask-text-textMuted">integrations.pexels.apiKey</div>
          </div>
        ) : null}

        <Field label={t('common.search')}>
          <div className="ask-flex ask-gap-2">
            <Input
              value={query}
              onInput={(e) => setQuery((e.currentTarget as HTMLInputElement).value)}
              placeholder={t('ui.search_pexels')}
              disabled={!key}
            />
            <Button
              onClick={() => {
                setPage(1);
                setHasMore(true);
                void fetchPage(query, 1, 'reset');
              }}
              disabled={!key || loading}
            >
              {loading ? t('common.loading') : t('common.search', 'Search')}
            </Button>
          </div>
        </Field>

        {error ? <div className="ask-text-sm ask-text-red-600">{error}</div> : null}

        <ScrollArea
          className="ask-h-[360px] ask-max-h-[min(55vh,360px)] ask-min-h-[220px] ask-rounded-xl ask-border ask-border-border/40"
          viewportClassName="ask-h-full ask-p-2 ask-pr-3"
          viewportRef={setScrollEl}
        >
          {gridItems.length > 0 ? (
            <ImageGrid
              items={gridItems}
              columns={2}
              onClick={(it) => {
                const p = items.find((x) => String(x.id) === it.id);
                if (p) addImageToCanvas(p);
              }}
            />
          ) : null}

          <div className="ask-mt-2 ask-flex ask-items-center ask-justify-between">
            <span className="ask-text-[11px] ask-text-textMuted">
              {loading ? t('common.loading') : loadingMore ? t('common.loadingMore') : gridItems.length === 0 ? t('ui.no_results_try_another_query') : hasMore ? t('common.scrollToLoadMore') : t('common.end')}
            </span>
            <Button onClick={loadMore} disabled={!key || loading || loadingMore || !hasMore} size="sm" variant="ghost">{t('common.loadMore')}</Button>
          </div>
        </ScrollArea>

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

registerModule({
  id: 'image-pexels',
  label: 'Pexels',
  icon: 'pexels',
  displayMode: 'floating',
  keepMounted: false,
  licenseFeature: 'pexels',
  supportedEngines: ['fabric', '3d'],
  requiredCapabilities: ['addImage'],
  isAvailable: Boolean(getPexelsApiKey()),
  render: PexelsPanel,
});

export default PexelsPanel;
