import { useCallback, useEffect, useRef, useState } from 'react';

/* ─────────────────────────────────────────────
   Constants
───────────────────────────────────────────── */
const PAGE_SIZE = 40;
const CACHE_TTL_MS = 10 * 60 * 1000; // 10 minutes

/* ─────────────────────────────────────────────
   Utility — normalise raw API items to a
   consistent photo shape
───────────────────────────────────────────── */
export const normalizePhotos = (items = []) =>
    items
        .map((item) => {
            if (typeof item === 'string') return { src: item, title: '', original: item, file: '', album: '' };
            if (!item) return null;
            return {
                src: item.thumb || item.url || item.path || item.src || '',
                title: item.title || item.name || item.file || '',
                original: item.url || item.path || item.src || '',
                file: item.file || '',
                album: item.album || '',
            };
        })
        .filter((item) => item && item.src);

/* ─────────────────────────────────────────────
   Internal helper — parse fetch JSON to
   normalised photo list + summary metadata
───────────────────────────────────────────── */
const parsePhotoResponse = (json) => {
    const items = Array.isArray(json) ? json : json.items ?? [];
    const meta = Array.isArray(json) ? {} : json ?? {};
    const normalized = normalizePhotos(items);
    const hasNext =
        typeof meta.has_next === 'boolean'
            ? meta.has_next
            : typeof meta.hasNext === 'boolean'
            ? meta.hasNext
            : normalized.length >= PAGE_SIZE;
    const summary =
        meta && (Object.prototype.hasOwnProperty.call(meta, 'total') ||
            Object.prototype.hasOwnProperty.call(meta, 'matched_albums'))
            ? { total: meta.total, albums: meta.matched_albums ?? [] }
            : null;
    return { normalized, hasNext, summary, matchedAlbums: meta.matched_albums ?? [] };
};

/* ─────────────────────────────────────────────
   useTravelData — data layer hook for the
   Travel gallery page
───────────────────────────────────────────── */
const useTravelData = () => {
    // ── Region & GeoJSON ─────────────────────
    const [regions, setRegions] = useState(null);          // GeoJSON FeatureCollection
    const [selectedRegion, setSelectedRegion] = useState(null); // { id, name }

    // ── Album list ───────────────────────────
    const [albums, setAlbums] = useState([]);              // built from per-region page-1 fetch
    const [loadingAlbums, setLoadingAlbums] = useState(false);

    // ── Photo list for selected album ────────
    const [photos, setPhotos] = useState([]);
    const [photoSummary, setPhotoSummary] = useState(null);
    const [loading, setLoading] = useState(false);
    const [loadingMore, setLoadingMore] = useState(false);
    const [hasNext, setHasNext] = useState(false);
    const [error, setError] = useState('');

    // ── Internal refs ────────────────────────
    const pageRef = useRef(1);
    const currentAlbumRef = useRef(null); // { regionId, albumName }
    const cacheRef = useRef(new Map());       // photo data cache  key: `${regionId}::${albumName}`
    const albumCacheRef = useRef(new Map());  // album metadata cache  key: regionId
    const loadAbortRef = useRef(null);        // AbortController for loadAlbumPhotos

    /* ── Load GeoJSON regions once ──────────── */
    useEffect(() => {
        const controller = new AbortController();
        (async () => {
            try {
                const res = await fetch('/api/travel/regions', { signal: controller.signal });
                if (!res.ok) throw new Error(`지역 정보 로딩 실패 (${res.status})`);
                const geojson = await res.json();
                setRegions(geojson);
            } catch (err) {
                if (err?.name !== 'AbortError') {
                    setError(err?.message ?? String(err));
                }
            }
        })();
        return () => controller.abort();
    }, []);

    /* ── Build album list from /api/travel/albums ── */
    const fetchAlbums = useCallback(async (signal) => {
        setLoadingAlbums(true);
        try {
            const res = await fetch('/api/travel/albums', { signal });
            if (!res.ok) throw new Error(`앨범 로딩 실패 (${res.status})`);
            const rows = await res.json();
            const builtAlbums = rows.map((r) => ({
                id: `${r.region}::${r.album}`,
                name: r.album,
                region: r.region,
                regionName: r.regionName || r.region,
                photoCount: r.count ?? 0,
                coverThumb: r.cover_thumb || '',
            }));
            setAlbums(builtAlbums);
        } catch (err) {
            if (err?.name !== 'AbortError') {
                setError(err?.message ?? String(err));
            }
        } finally {
            setLoadingAlbums(false);
        }
    }, []);

    useEffect(() => {
        const controller = new AbortController();
        fetchAlbums(controller.signal);
        return () => controller.abort();
    }, [fetchAlbums]);

    /* ── loadAlbumPhotos — initial load ────── */
    const loadAlbumPhotos = useCallback(async (regionId, albumName) => {
        if (!regionId) return;

        const cacheKey = `${regionId}::${albumName ?? ''}`;
        currentAlbumRef.current = { regionId, albumName };

        // Check photo cache
        const cached = cacheRef.current.get(cacheKey);
        if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
            setPhotos(cached.items);
            setPhotoSummary(cached.summary ?? null);
            pageRef.current = cached.page ?? 2;
            setHasNext(cached.hasNext ?? false);
            setLoading(false);
            setLoadingMore(false);
            setError('');
            return;
        }

        setLoading(true);
        setLoadingMore(false);
        setError('');
        setPhotos([]);
        setPhotoSummary(null);
        setHasNext(false);
        pageRef.current = 1;

        // Abort any in-flight loadAlbumPhotos request
        if (loadAbortRef.current) loadAbortRef.current.abort();
        const controller = new AbortController();
        loadAbortRef.current = controller;
        try {
            let url = `/api/travel/photos?region=${encodeURIComponent(regionId)}&page=1&size=${PAGE_SIZE}`;
            if (albumName) url += `&album=${encodeURIComponent(albumName)}`;

            const res = await fetch(url, { signal: controller.signal });
            if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
            const json = await res.json();
            const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);

            // Filter by album name client-side when API doesn't support album param
            const filtered = albumName
                ? normalized.filter((p) => !p.album || p.album === albumName)
                : normalized;

            pageRef.current = 2;
            setPhotos(filtered);
            setPhotoSummary(summary);
            setHasNext(hn);

            cacheRef.current.set(cacheKey, {
                timestamp: Date.now(),
                items: filtered,
                page: 2,
                hasNext: hn,
                summary,
            });
        } catch (err) {
            if (err?.name === 'AbortError') return;
            setError(err?.message ?? String(err));
            setPhotos([]);
            setPhotoSummary(null);
        } finally {
            setLoading(false);
        }
    }, []);

    /* ── loadMorePhotos — infinite scroll ──── */
    const loadMorePhotos = useCallback(async (regionId, albumName) => {
        const activeRegion = regionId ?? currentAlbumRef.current?.regionId;
        const activeAlbum = albumName ?? currentAlbumRef.current?.albumName;
        if (!activeRegion || loading || loadingMore || !hasNext) return;

        setLoadingMore(true);
        setError('');

        const moreController = new AbortController();
        try {
            let url = `/api/travel/photos?region=${encodeURIComponent(activeRegion)}&page=${pageRef.current}&size=${PAGE_SIZE}`;
            if (activeAlbum) url += `&album=${encodeURIComponent(activeAlbum)}`;

            const res = await fetch(url, { signal: moreController.signal });
            if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
            const json = await res.json();
            const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);

            // Filter by album name client-side
            const filtered = activeAlbum
                ? normalized.filter((p) => !p.album || p.album === activeAlbum)
                : normalized;

            const cacheKey = `${activeRegion}::${activeAlbum ?? ''}`;
            setPhotos((prev) => {
                const merged = [...prev, ...filtered];
                cacheRef.current.set(cacheKey, {
                    timestamp: Date.now(),
                    items: merged,
                    page: pageRef.current + 1,
                    hasNext: hn,
                    summary: photoSummary ?? summary,
                });
                return merged;
            });
            if (!photoSummary && summary) setPhotoSummary(summary);
            setHasNext(hn);
            pageRef.current += 1;
        } catch (err) {
            setError(err?.message ?? String(err));
        } finally {
            setLoadingMore(false);
        }
    }, [hasNext, loading, loadingMore, photoSummary]);

    /* ── reloadAlbumPhotos — pull-to-refresh ─ */
    const reloadAlbumPhotos = useCallback(async (regionId, albumName) => {
        const activeRegion = regionId ?? currentAlbumRef.current?.regionId;
        const activeAlbum = albumName ?? currentAlbumRef.current?.albumName;
        if (!activeRegion) return;

        const cacheKey = `${activeRegion}::${activeAlbum ?? ''}`;
        cacheRef.current.delete(cacheKey);

        let url = `/api/travel/photos?region=${encodeURIComponent(activeRegion)}&page=1&size=${PAGE_SIZE}`;
        if (activeAlbum) url += `&album=${encodeURIComponent(activeAlbum)}`;

        const reloadController = new AbortController();
        try {
            const res = await fetch(url, { signal: reloadController.signal });
            if (!res.ok) throw new Error(`사진 로딩 실패 (${res.status})`);
            const json = await res.json();
            const { normalized, hasNext: hn, summary } = parsePhotoResponse(json);

            const filtered = activeAlbum
                ? normalized.filter((p) => !p.album || p.album === activeAlbum)
                : normalized;

            pageRef.current = 2;
            setPhotos(filtered);
            setHasNext(hn);
            cacheRef.current.set(cacheKey, {
                timestamp: Date.now(),
                items: filtered,
                page: 2,
                hasNext: hn,
                summary,
            });
            if (summary) setPhotoSummary(summary);
        } catch (err) {
            if (err?.name === 'AbortError') return;
            setError(err?.message ?? String(err));
        }
    }, []);

    /* ── getFilteredAlbums — filter by region ─ */
    const getFilteredAlbums = useCallback(
        (regionId) => {
            if (!regionId) return albums;
            return albums.filter((a) => a.region === regionId);
        },
        [albums]
    );

    return {
        // GeoJSON data
        regions,

        // Album list
        albums,
        loadingAlbums,

        // Region filter
        selectedRegion,
        setSelectedRegion,

        // Photo data
        photos,
        photoSummary,

        // Loading states
        loading,
        loadingMore,

        // Error
        error,

        // Pagination
        hasNext,

        // Actions
        loadAlbumPhotos,
        loadMorePhotos,
        reloadAlbumPhotos,
        getFilteredAlbums,
        refreshAlbums: () => fetchAlbums(),
    };
};

export default useTravelData;
