
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 { getFacebookConfig } from '../../core/integrations';
import { facebookLogin, facebookLogout, getStoredFacebookToken, graphGet, type GraphEdgeResponse } from '../../core/facebook';
import { useInfiniteScroll } from '../../core/hooks/useInfiniteScroll';
import { resolveRemoteImageUrlWithUpload } from '../../core/images';

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

type FbAlbum = {
  id: string;
  name?: string;
  count?: number;
  cover_photo?: { picture?: string } | null;
};

type FbPhoto = {
  id: string;
  name?: string;
  picture?: string;
  images?: { height: number; width: number; source: string }[];
};

const PER_PAGE = 24;

function bestSource(p: FbPhoto): string | null {
  const imgs = p?.images;
  if (Array.isArray(imgs) && imgs.length > 0) return imgs[0]?.source ?? null;
  return null;
}

export const FacebookPanel: AsukaModuleComponent = ({ context }) => {
  const t = useT();
  const engine = context.getEngine?.() ?? null;
  const permissions = useAsukaStore((s: any) => s.permissions ?? DEFAULT_PERMISSIONS);
  const cfg = useMemo(() => getFacebookConfig(), []);
  const [token, setToken] = useState<string | null>(() => getStoredFacebookToken());

  const [albums, setAlbums] = useState<FbAlbum[]>([]);
  const [activeAlbumId, setActiveAlbumId] = useState<string>('__uploaded__');

  const [photos, setPhotos] = useState<FbPhoto[]>([]);
  const [loading, setLoading] = useState(false);
  const [loadingMore, setLoadingMore] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [nextUrl, setNextUrl] = useState<string | null>(null);
  const [scrollEl, setScrollEl] = useState<HTMLDivElement | null>(null);

  const requestIdRef = useRef(0);
  const requestAbortRef = useRef<AbortController | null>(null);
  const loadMoreAbortRef = useRef<AbortController | null>(null);
  const inFlightMoreRef = useRef(false);
  const mountedRef = useRef(true);
  const didInitialPhotosRef = useRef(false);
  const skipNextAlbumEffectRef = useRef(false);

  useEffect(() => {
    return () => {
      mountedRef.current = false;
      requestAbortRef.current?.abort();
      requestAbortRef.current = null;
      loadMoreAbortRef.current?.abort();
      loadMoreAbortRef.current = null;
      inFlightMoreRef.current = false;
    };
  }, []);

  useEffect(() => {
    const onStorage = (e: StorageEvent) => {
      if (e.key && e.key.includes('asuka.facebook.access_token')) setToken(getStoredFacebookToken());
    };
    window.addEventListener('storage', onStorage);
    return () => window.removeEventListener('storage', onStorage);
  }, []);

  const isConfigured = Boolean(cfg?.appId);

  const onConnect = useCallback(async () => {
    setError(null);
    try {
      await facebookLogin();
      if (mountedRef.current) setToken(getStoredFacebookToken());
    } catch (e: any) {
      if (mountedRef.current) setError(e?.message ?? String(e));
    }
  }, []);

  const onDisconnect = useCallback(async () => {
    setError(null);
    try {
      await facebookLogout();
    } catch {
      // ignore
    }
    if (!mountedRef.current) return;
    requestAbortRef.current?.abort();
    loadMoreAbortRef.current?.abort();
    setToken(null);
    setAlbums([]);
    setPhotos([]);
    setNextUrl(null);
    setActiveAlbumId('__uploaded__');
  }, []);

  const fetchAlbums = useCallback(async (requestId: number, signal?: AbortSignal) => {
    try {
      const res = await graphGet<GraphEdgeResponse<FbAlbum>>('me/albums', {
        fields: 'id,name,count,cover_photo{picture}',
        limit: 50,
      }, { signal });
      if (!mountedRef.current || requestId !== requestIdRef.current) return;
      setAlbums(res?.data ?? []);
    } catch (e: any) {
      if (e?.name === 'AbortError') return;
      if (!mountedRef.current || requestId !== requestIdRef.current) return;
      setError(e?.message ?? String(e));
    }
  }, []);

  const fetchPhotosFirstPage = useCallback(async (albumId: string, requestId: number, signal?: AbortSignal) => {
    try {
      const edge = albumId === '__uploaded__' ? 'me/photos' : `${albumId}/photos`;
      const res = await graphGet<GraphEdgeResponse<FbPhoto>>(edge, {
        fields: 'id,name,picture,images',
        limit: PER_PAGE,
        ...(albumId === '__uploaded__' ? { type: 'uploaded' } : {}),
      }, { signal });
      if (!mountedRef.current || requestId !== requestIdRef.current) return;
      setPhotos(res?.data ?? []);
      setNextUrl(res?.paging?.next ?? null);
    } catch (e: any) {
      if (e?.name === 'AbortError') return;
      if (!mountedRef.current || requestId !== requestIdRef.current) return;
      setError(e?.message ?? String(e));
      setPhotos([]);
      setNextUrl(null);
    }
  }, []);

  const refreshFacebookData = useCallback(async (albumId: string, includeAlbums = false) => {
    if (!token) return;
    const requestId = requestIdRef.current + 1;
    requestIdRef.current = requestId;
    requestAbortRef.current?.abort();
    const ac = new AbortController();
    requestAbortRef.current = ac;
    loadMoreAbortRef.current?.abort();
    inFlightMoreRef.current = false;
    setError(null);
    setLoading(true);
    setLoadingMore(false);
    try {
      if (includeAlbums) await fetchAlbums(requestId, ac.signal);
      await fetchPhotosFirstPage(albumId, requestId, ac.signal);
    } finally {
      if (mountedRef.current && requestId === requestIdRef.current) setLoading(false);
    }
  }, [fetchAlbums, fetchPhotosFirstPage, token]);

  useEffect(() => {
    if (!token) {
      didInitialPhotosRef.current = false;
      skipNextAlbumEffectRef.current = false;
      return;
    }
    didInitialPhotosRef.current = true;
    skipNextAlbumEffectRef.current = true;
    void refreshFacebookData(activeAlbumId, true);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token]);

  useEffect(() => {
    if (!token || !didInitialPhotosRef.current) return;
    if (skipNextAlbumEffectRef.current) {
      skipNextAlbumEffectRef.current = false;
      return;
    }
    void refreshFacebookData(activeAlbumId, false);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeAlbumId]);

  const loadMore = useCallback(async () => {
    if (!nextUrl || loading || loadingMore || inFlightMoreRef.current) return;

    const requestId = requestIdRef.current;
    const ac = new AbortController();
    loadMoreAbortRef.current?.abort();
    loadMoreAbortRef.current = ac;
    inFlightMoreRef.current = true;
    setLoadingMore(true);
    setError(null);

    try {
      const r = await fetch(nextUrl, { signal: ac.signal });
      const j = await r.json();
      if (!r.ok || j?.error) throw new Error(j?.error?.message ?? `Graph API error (${r.status})`);
      if (ac.signal.aborted || !mountedRef.current || requestId !== requestIdRef.current) return;
      const data = (j?.data ?? []) as FbPhoto[];
      setPhotos((prev) => {
        const map = new Map(prev.map((p) => [p.id, p]));
        for (const p of data) map.set(p.id, p);
        return Array.from(map.values());
      });
      setNextUrl(j?.paging?.next ?? null);
    } catch (e: any) {
      if (e?.name === 'AbortError') return;
      if (!mountedRef.current || requestId !== requestIdRef.current) return;
      setError(e?.message ?? String(e));
      setNextUrl(null);
    } finally {
      if (mountedRef.current && requestId === requestIdRef.current) {
        inFlightMoreRef.current = false;
        setLoadingMore(false);
      }
    }
  }, [loading, loadingMore, nextUrl]);

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

  const items: ImageGridItem[] = useMemo(() => {
    return (photos ?? [])
      .map((p) => ({
        id: p.id,
        src: p.picture || bestSource(p) || '',
        alt: p.name || 'Facebook photo',
        badge: 'FB',
      }))
      .filter((it) => Boolean(it.src));
  }, [photos]);

  const onPickPhoto = useCallback(async (it: ImageGridItem) => {
    if (!((permissions as any)?.canAddImage ?? true) || !((permissions as any)?.canUseExternalImageLibraries ?? true) || !((permissions as any)?.canUseFacebookImages ?? true)) return;
    if (!engine) return;
    const p = photos.find((x) => x.id === it.id);
    const url = p ? (bestSource(p) || p.picture) : it.src;
    if (!url) return;
    try {
      const resolvedUrl = await resolveRemoteImageUrlWithUpload(url, { source: 'facebook', name: p?.name || `facebook-${it.id}` });
      await engine.objects.addImage({ url: resolvedUrl, center: true, metadata: { sourceModule: 'facebook' } } as any);
    } catch {}
  }, [engine, permissions, photos]);

  return (
    <Card>
      <CardHeader className="ask-flex ask-items-center ask-justify-between ask-gap-2">
        <CardTitle>{t('ui.facebook')}</CardTitle>
        {token ? (
          <div className="ask-flex ask-items-center ask-gap-2">
            <Button size="sm" variant="secondary" onClick={onDisconnect}>{t('ui.disconnect')}</Button>
            <Button size="sm" onClick={onConnect} disabled={!isConfigured}>{t('ui.connect')}</Button>
          </div>
        ) : (
          <Button size="sm" onClick={onConnect} disabled={!isConfigured}>{t('ui.connect')}</Button>
        )}
      </CardHeader>

      <CardContent className="ask-flex ask-flex-col ask-gap-4">
        {!isConfigured ? (
          <div className="ask-rounded-[14px] ask-border ask-border-borderSubtle ask-bg-surfaceAlt ask-p-3 ask-text-[11px] ask-text-textMuted">
            {t('facebook.notConfiguredPrefix', 'Facebook integration is not configured. Pass')} <code className="ask-mx-1 ask-text-[11px]">integrations.facebook.appId</code> {t('facebook.notConfiguredSuffix', 'at initialization to enable this tab.')}
          </div>
        ) : !token ? (
          <div className="ask-text-[12px] ask-text-textMuted ask-leading-5">
            {t('facebook.connectHint', 'Connect your Facebook account to browse your photos and albums.')}
            <div className="ask-mt-2">{t('facebook.graphApiHint', 'This uses Facebook Login and the Graph API.')}</div>
            {error ? <div className="ask-mt-2 ask-text-red-600">{error}</div> : null}
          </div>
        ) : (
          <>
            <div className="ask-flex ask-items-center ask-gap-2">
              <Field label={t('ui.source')} className="ask-flex-1">
                <select
                  className="ask-theme-select ask-w-full ask-rounded-[14px] ask-border ask-px-3 ask-py-2 ask-text-sm"
                  value={activeAlbumId}
                  onChange={(e) => setActiveAlbumId((e.currentTarget as HTMLSelectElement).value)}
                >
                  <option value="__uploaded__">{t('ui.uploaded_photos')}</option>
                  {albums.map((a) => (
                    <option key={a.id} value={a.id}>
                      {a.name ?? 'Album'}
                      {typeof a.count === 'number' ? ` (${a.count})` : ''}
                    </option>
                  ))}
                </select>
              </Field>
            </div>

            {error ? <div className="ask-text-[11px] 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}
            >
              {items.length > 0 ? (
                <ImageGrid items={items} columns={2} onClick={onPickPhoto} />
              ) : (
                <div className="ask-text-[11px] ask-text-textMuted">{loading ? t('common.loading') : 'No photos found.'}</div>
              )}

              <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') : nextUrl ? t('common.scrollToLoadMore') : t('common.end')}
                </span>
                {nextUrl ? (
                  <Button variant="ghost" size="sm" onClick={loadMore} disabled={loading || loadingMore}>
                    {loadingMore ? t('common.loading') : t('common.loadMore')}
                  </Button>
                ) : null}
              </div>
            </ScrollArea>
          </>
        )}
      </CardContent>
    </Card>
  );
};

registerModule({
  id: 'image-facebook',
  label: 'Facebook',
  icon: 'facebook',
  displayMode: 'floating',
  licenseFeature: 'facebookImages',
  supportedEngines: ['fabric', '3d'],
  requiredCapabilities: ['addImage'],
  render: FacebookPanel,
});

export default FacebookPanel;
