// Sceny z konkretnego tube/source — listScenes z origin substring filter.
// Bug-report 2026-05-24 (ea6f05f9): top-level Sites browse → tap site → tutaj.
//
// Sort: release_date DESC żeby user dostał świeże publikacje na górze. Sceny bez
// release_date dryfują na koniec — to znany trade-off (patrz freshporno backfill
// 2026-05-23, 10390 scen miało null date).
//
// Infinite scroll bo niektóre tubey mają 100k+ scen (porntrex, xvideos).
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import React, { useState } from 'react';
import * as Haptics from 'expo-haptics';
import {
  ActivityIndicator,
  FlatList,
  Modal,
  Pressable,
  ScrollView,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';
import { SceneTile } from '../components/SceneTile';
import { Thumb } from '../components/Thumb';
import { useClient } from '../ClientContext';
import type { RootStackParamList } from '../navigation';
import { theme } from '../theme';
import type { SceneOut, TagOut } from '../types';

export function SiteScenesScreen() {
  const client = useClient();
  const navigation =
    useNavigation<NativeStackNavigationProp<RootStackParamList, 'SiteScenes'>>();
  const route = useRoute<RouteProp<RootStackParamList, 'SiteScenes'>>();
  const { origin, name } = route.params;

  React.useLayoutEffect(() => {
    navigation.setOptions({ title: name });
  }, [navigation, name]);

  // Tag filter — user-report 2026-05-26 (43f81a46) "Przydałyby się kategorie na
  // stronach Sites". Multi-select AND (passed as comma-CSV do `tags` query).
  const [selectedTags, setSelectedTags] = useState<string[]>([]);
  const [filterOpen, setFilterOpen] = useState(false);

  const PER_PAGE = 50;
  const {
    data,
    isLoading,
    error,
    refetch,
    isRefetching,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['site-scenes', origin, selectedTags.sort().join(',')],
    queryFn: ({ pageParam = 1 }) =>
      client.listScenes({
        origin,
        tags: selectedTags.length > 0 ? selectedTags : undefined,
        sort: 'release_date',
        page: pageParam,
        per_page: PER_PAGE,
      }),
    initialPageParam: 1,
    getNextPageParam: (lastPage) => {
      // Paginuj po has_more (z fetcha per_page+1). `total` bywa bounded ("1000+").
      const more =
        lastPage.has_more ?? lastPage.page * lastPage.per_page < lastPage.total;
      return more ? lastPage.page + 1 : undefined;
    },
  });
  const items = data?.pages.flatMap((p) => p.items) ?? [];
  const total = data?.pages[0]?.total ?? 0;
  // total bywa bounded ("1000+") dla dużych site'ów — patrz backend _COUNT_CAP.
  const totalLabel = `${total}${data?.pages[0]?.total_capped ? '+' : ''}`;

  return (
    <View style={styles.container}>
      <View style={styles.toolbar}>
        <Pressable
          style={[styles.filterBtn, selectedTags.length > 0 && styles.filterBtnActive]}
          onPress={() => setFilterOpen(true)}
        >
          <Text style={styles.filterBtnText}>
            Tags{selectedTags.length > 0 ? ` ${selectedTags.length}` : ''}
          </Text>
        </Pressable>
        {selectedTags.length > 0 ? (
          <Pressable style={styles.clearBtn} onPress={() => setSelectedTags([])}>
            <Text style={styles.clearBtnText}>Clear</Text>
          </Pressable>
        ) : null}
      </View>

      <TagPickerModal
        visible={filterOpen}
        initialSelected={selectedTags}
        onApply={(next) => {
          setSelectedTags(next);
          setFilterOpen(false);
        }}
        onClose={() => setFilterOpen(false)}
      />

      {isLoading && <ActivityIndicator color={theme.fg} style={{ marginTop: 24 }} />}
      {error instanceof Error && <Text style={styles.error}>{error.message}</Text>}

      <FlatList
        data={items}
        keyExtractor={(s) => s.id}
        numColumns={2}
        removeClippedSubviews={false}
        renderItem={({ item }) => <SceneTile scene={item} secondLine="studio" />}
        columnWrapperStyle={styles.gridRow}
        refreshing={isRefetching}
        onRefresh={refetch}
        onEndReached={() => {
          if (hasNextPage && !isFetchingNextPage) fetchNextPage();
        }}
        onEndReachedThreshold={0.5}
        ListHeaderComponent={
          data ? (
            <Text style={styles.subtitle}>
              {totalLabel} {total === 1 ? 'scene' : 'scenes'} · sorted by release date
            </Text>
          ) : null
        }
        ListFooterComponent={
          isFetchingNextPage ? (
            <ActivityIndicator color={theme.muted} style={{ marginVertical: 18 }} />
          ) : !hasNextPage && items.length > 0 ? (
            <Text style={styles.muted}>{`${items.length} / ${totalLabel}`}</Text>
          ) : null
        }
        ListEmptyComponent={!isLoading ? <Text style={styles.muted}>no scenes</Text> : null}
        contentContainerStyle={{ paddingBottom: 24 }}
      />
    </View>
  );
}

function TagPickerModal({
  visible,
  initialSelected,
  onApply,
  onClose,
}: {
  visible: boolean;
  initialSelected: string[];
  onApply: (selected: string[]) => void;
  onClose: () => void;
}) {
  const client = useClient();
  const [selected, setSelected] = useState<string[]>(initialSelected);
  const [q, setQ] = useState('');

  React.useEffect(() => {
    if (visible) setSelected(initialSelected);
  }, [visible, initialSelected]);

  const { data, isLoading } = useQuery({
    queryKey: ['tags-popular', q],
    queryFn: () =>
      client.listTags({
        q: q.trim() || undefined,
        order: 'popular',
        per_page: 200,
        only_with_content: true,
      }),
    enabled: visible,
  });
  const tags: TagOut[] = data?.items ?? [];

  const toggle = (slug: string) => {
    setSelected((prev) =>
      prev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug],
    );
  };

  return (
    <Modal visible={visible} animationType="slide" onRequestClose={onClose} transparent>
      <View style={modalStyles.backdrop}>
        <View style={modalStyles.sheet}>
          <View style={modalStyles.header}>
            <Text style={modalStyles.title}>Filter by tags</Text>
            <Pressable onPress={onClose}>
              <Text style={modalStyles.close}>✕</Text>
            </Pressable>
          </View>
          <TextInput
            style={modalStyles.search}
            placeholder="Search tags…"
            placeholderTextColor={theme.muted}
            value={q}
            onChangeText={setQ}
            autoCapitalize="none"
          />
          {isLoading ? (
            <ActivityIndicator color={theme.fg} style={{ marginTop: 16 }} />
          ) : (
            <ScrollView style={modalStyles.chipScroll} contentContainerStyle={modalStyles.chipRow}>
              {tags.map((t) => {
                const on = selected.includes(t.slug);
                return (
                  <Pressable
                    key={t.id}
                    onPress={() => toggle(t.slug)}
                    style={[modalStyles.chip, on && modalStyles.chipOn]}
                  >
                    <Text style={[modalStyles.chipText, on && modalStyles.chipTextOn]}>
                      {t.name}
                    </Text>
                  </Pressable>
                );
              })}
              {tags.length === 0 ? (
                <Text style={modalStyles.muted}>No results</Text>
              ) : null}
            </ScrollView>
          )}
          <View style={modalStyles.footer}>
            <Pressable
              style={modalStyles.footerBtn}
              onPress={() => setSelected([])}
              disabled={selected.length === 0}
            >
              <Text
                style={[modalStyles.footerBtnText, selected.length === 0 && { opacity: 0.4 }]}
              >
                Clear
              </Text>
            </Pressable>
            <Pressable
              style={[modalStyles.footerBtn, modalStyles.footerBtnPrimary]}
              onPress={() => onApply(selected)}
            >
              <Text style={modalStyles.footerBtnTextPrimary}>
                Apply{selected.length > 0 ? ` (${selected.length})` : ''}
              </Text>
            </Pressable>
          </View>
        </View>
      </View>
    </Modal>
  );
}

function SceneRow({ scene }: { scene: SceneOut }) {
  const navigation =
    useNavigation<NativeStackNavigationProp<RootStackParamList, 'SiteScenes'>>();
  const [isPreviewing, setIsPreviewing] = useState(false);
  const performers = scene.performers
    .slice(0, 3)
    .map((p) => p.canonical_name)
    .join(', ');

  const animatedUrl = scene.playback_sources.find((s) => s.animated_thumbnail_url)
    ?.animated_thumbnail_url;
  const staticUrl = scene.playback_sources.find((s) => s.thumbnail_url)?.thumbnail_url;
  const displayUrl = isPreviewing && animatedUrl ? animatedUrl : staticUrl ?? animatedUrl;

  const startPreview = () => {
    if (!animatedUrl) return;
    setIsPreviewing(true);
    Haptics.selectionAsync().catch(() => {});
  };

  const dim = scene.finished === true;

  return (
    <Pressable
      style={[styles.row, dim && styles.rowDimmed]}
      onPress={() => navigation.push('SceneDetail', { id: scene.id })}
      onLongPress={startPreview}
      onPressOut={() => setIsPreviewing(false)}
      delayLongPress={180}
    >
      <Thumb url={displayUrl} style={styles.thumbnail} />
      {scene.is_favorite ? (
        <View style={styles.favBadge}>
          <Text style={styles.favBadgeText}>★</Text>
        </View>
      ) : null}
      <View style={styles.rowContent}>
        <Text style={styles.rowTitle} numberOfLines={1}>
          {scene.title}
        </Text>
        {scene.release_date || scene.studio ? (
          <Text style={styles.rowMuted} numberOfLines={1}>
            {[scene.release_date, scene.studio?.name].filter(Boolean).join(' · ')}
          </Text>
        ) : null}
        {performers ? (
          <Text style={styles.rowMuted} numberOfLines={1}>
            {performers}
            {scene.performers.length > 3 ? ` +${scene.performers.length - 3}` : ''}
          </Text>
        ) : null}
        <Text style={styles.rowSources}>
          {[...new Set(scene.external_refs.map((r) => r.source))].join(' · ')}
          {scene.playback_sources.length > 0
            ? `  ▶ ${scene.playback_sources.length}`
            : ''}
          {dim ? '  ✓ watched' : ''}
        </Text>
      </View>
    </Pressable>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: theme.bg, paddingHorizontal: 12, paddingTop: 8 },
  subtitle: { color: theme.muted, marginBottom: 8, paddingHorizontal: 4 },
  row: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
    borderRadius: 12,
    padding: 12,
    marginBottom: 10,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.2,
    shadowRadius: 4,
    elevation: 3,
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
  },
  rowDimmed: { opacity: 0.45 },
  thumbnail: {
    width: 100,
    height: 56,
    borderRadius: 8,
    backgroundColor: theme.border,
  },
  favBadge: {
    position: 'absolute',
    top: 6,
    left: 6,
    backgroundColor: 'rgba(0,0,0,0.7)',
    paddingHorizontal: 5,
    paddingVertical: 1,
    borderRadius: 8,
  },
  favBadgeText: { color: theme.accent, fontSize: 12, fontWeight: '700' },
  rowContent: { flex: 1 },
  rowTitle: { color: theme.fg, fontWeight: '700', fontSize: 16, marginBottom: 4 },
  rowMuted: { color: theme.muted, fontSize: 14, marginTop: 2 },
  rowSources: {
    color: theme.accent,
    fontSize: 12,
    marginTop: 8,
    textTransform: 'uppercase',
    fontWeight: '600',
  },
  muted: { color: theme.muted, textAlign: 'center', marginTop: 24, fontSize: 14 },
  error: { color: theme.bad, padding: 12 },
  toolbar: { flexDirection: 'row', gap: 8, marginBottom: 8, alignItems: 'center' },
  gridRow: { gap: 10, marginBottom: 14 },
  filterBtn: {
    paddingHorizontal: 14,
    paddingVertical: 8,
    borderRadius: 10,
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
  },
  filterBtnActive: { backgroundColor: theme.accentDeep, borderColor: theme.accent },
  filterBtnText: { color: theme.fg, fontSize: 13, fontWeight: '600' },
  clearBtn: { paddingHorizontal: 10, paddingVertical: 8 },
  clearBtnText: { color: theme.muted, fontSize: 13 },
});

const modalStyles = StyleSheet.create({
  backdrop: { flex: 1, backgroundColor: 'rgba(0,0,0,0.6)', justifyContent: 'flex-end' },
  sheet: {
    backgroundColor: theme.bg,
    borderTopLeftRadius: 16,
    borderTopRightRadius: 16,
    maxHeight: '80%',
    padding: 16,
  },
  header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 },
  title: { color: theme.fg, fontSize: 17, fontWeight: '700' },
  close: { color: theme.muted, fontSize: 20, padding: 4 },
  search: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
    borderRadius: 10,
    color: theme.fg,
    paddingHorizontal: 12,
    paddingVertical: 10,
    fontSize: 14,
    marginBottom: 12,
  },
  chipScroll: { maxHeight: 380 },
  chipRow: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, paddingBottom: 8 },
  chip: {
    paddingHorizontal: 12,
    paddingVertical: 7,
    borderRadius: 999,
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
  },
  chipOn: { backgroundColor: theme.accentDeep, borderColor: theme.accent },
  chipText: { color: theme.fg, fontSize: 13 },
  chipTextOn: { color: theme.fg, fontWeight: '700' },
  muted: { color: theme.muted, fontSize: 13, paddingVertical: 8 },
  footer: { flexDirection: 'row', gap: 8, marginTop: 12, justifyContent: 'flex-end' },
  footerBtn: { paddingHorizontal: 14, paddingVertical: 10, borderRadius: 10 },
  footerBtnText: { color: theme.muted, fontSize: 14, fontWeight: '600' },
  footerBtnPrimary: { backgroundColor: theme.accent },
  footerBtnTextPrimary: { color: theme.bg, fontSize: 14, fontWeight: '700' },
});
