import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import React, { useState } from 'react';
import {
  ActivityIndicator,
  Alert,
  FlatList,
  Pressable,
  ScrollView,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';
import { Image } from 'expo-image';
import * as Haptics from 'expo-haptics';
import { SceneTile, sceneGridProps } from '../components/SceneTile';
import { SceneGridSkeleton } from '../components/SceneGridSkeleton';
import { Thumb } from '../components/Thumb';
import { useClient } from '../ClientContext';
import { usePreferences } from '../PreferencesContext';
import { theme } from '../theme';
import type { RootStackParamList } from '../navigation';
import type { SceneOut } from '../types';
import { DEFAULT_FILTER, FilterState, ScenesFilterModal } from './ScenesFilterModal';

export function ScenesScreen() {
  const client = useClient();
  const { gridColumns } = usePreferences();
  const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Scenes'>>();
  const [q, setQ] = useState('');
  const [debouncedQ, setDebouncedQ] = useState('');
  const [filter, setFilter] = useState<FilterState>(DEFAULT_FILTER);
  const [filterOpen, setFilterOpen] = useState(false);

  React.useEffect(() => {
    const t = setTimeout(() => setDebouncedQ(q), 350);
    return () => clearTimeout(t);
  }, [q]);

  const PER_PAGE = 50;
  const {
    data,
    isLoading,
    error,
    refetch,
    isRefetching,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery({
    queryKey: ['scenes', debouncedQ, filter],
    queryFn: ({ pageParam = 1 }) =>
      client.listScenes({
        q: debouncedQ || undefined,
        tags: filter.tagSlugs.length ? filter.tagSlugs : undefined,
        studio_slugs: filter.studioSlugs.length ? filter.studioSlugs : undefined,
        performer_ids: filter.performerIds.length ? filter.performerIds : undefined,
        has_playback: filter.hasPlayback || undefined,
        sort: filter.sort,
        include_stubs: filter.includeStubs || undefined,
        origin: filter.origin.trim() || undefined,
        // 0 = "Any" → undefined → backend stosuje bazowy próg 60s. >0 podnosi minimum.
        min_duration_sec: filter.minDurationSec > 0 ? filter.minDurationSec : undefined,
        page: pageParam,
        per_page: PER_PAGE,
      }),
    initialPageParam: 1,
    getNextPageParam: (lastPage) => {
      // Paginuj po has_more (źródło prawdy z fetcha per_page+1). `total` jest dla
      // list filtrowanych bounded ("1000+"), więc NIE nadaje się do liczenia stron.
      // Fallback na loaded<total gdy stary backend nie zwraca has_more.
      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 list filtrowanych (q/tag) — patrz backend _COUNT_CAP.
  const totalLabel = `${total}${data?.pages[0]?.total_capped ? '+' : ''}`;

  const activeCount =
    filter.tagSlugs.length +
    filter.studioSlugs.length +
    filter.performerIds.length +
    (filter.hasPlayback ? 1 : 0) +
    (filter.origin.trim() ? 1 : 0);

  // Saved searches — zapisane słowa kluczowe (user-report mobilism).
  const queryClient = useQueryClient();
  const savedQuery = useQuery({
    queryKey: ['saved-searches'],
    queryFn: () => client.listSavedSearches(),
  });
  const saved = savedQuery.data ?? [];
  const addSaved = useMutation({
    mutationFn: (query: string) => client.addSavedSearch(query),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['saved-searches'] }),
  });
  const removeSaved = useMutation({
    mutationFn: (id: string) => client.removeSavedSearch(id),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['saved-searches'] }),
  });
  const applySaved = (query: string) => {
    setQ(query);
    setDebouncedQ(query); // natychmiast, bez czekania na debounce
  };
  const alreadySaved = saved.some((s) => s.query === q.trim());

  return (
    <View style={styles.container}>
      <View style={styles.toolbar}>
        <TextInput
          style={styles.search}
          value={q}
          onChangeText={setQ}
          placeholder="search title…"
          placeholderTextColor={theme.muted}
          autoCapitalize="none"
        />
        {q.trim().length > 0 ? (
          <Pressable
            onPress={() => {
              const v = q.trim();
              if (v && !alreadySaved) addSaved.mutate(v);
            }}
            disabled={alreadySaved}
            style={[styles.toolbarButton, alreadySaved && styles.toolbarButtonActive]}
          >
            <Text
              style={[styles.toolbarButtonText, alreadySaved && { color: theme.accent }]}
            >
              {alreadySaved ? '★ Saved' : '☆ Save'}
            </Text>
          </Pressable>
        ) : null}
        <Pressable
          onPress={() => setFilterOpen(true)}
          style={[styles.toolbarButton, activeCount > 0 && styles.toolbarButtonActive]}
        >
          <Text style={[styles.toolbarButtonText, activeCount > 0 && { color: theme.accent }]}>
            Filter{activeCount > 0 ? ` ${activeCount}` : ''}
          </Text>
        </Pressable>
      </View>

      {saved.length > 0 ? (
        <ScrollView
          horizontal
          showsHorizontalScrollIndicator={false}
          style={styles.savedRow}
          contentContainerStyle={styles.savedRowContent}
        >
          {saved.map((s) => {
            const active = debouncedQ === s.query;
            return (
              <Pressable
                key={s.id}
                style={[styles.savedChip, active && styles.savedChipActive]}
                onPress={() => applySaved(s.query)}
                onLongPress={() =>
                  Alert.alert('Remove saved search', `Remove "${s.query}"?`, [
                    { text: 'Cancel', style: 'cancel' },
                    {
                      text: 'Remove',
                      style: 'destructive',
                      onPress: () => removeSaved.mutate(s.id),
                    },
                  ])
                }
              >
                <Text
                  style={[styles.savedChipText, active && styles.savedChipTextActive]}
                  numberOfLines={1}
                >
                  {s.query}
                </Text>
              </Pressable>
            );
          })}
        </ScrollView>
      ) : null}

      <View style={styles.subToolbar}>
        <Pressable
          style={styles.subButton}
          onPress={() => navigation.navigate('Performers')}
        >
          <Text style={styles.subButtonText}>Performers</Text>
        </Pressable>
        <Pressable
          style={styles.subButton}
          onPress={() => navigation.navigate('Tags')}
        >
          <Text style={styles.subButtonText}>Tags</Text>
        </Pressable>
        <FavoritesButton />
      </View>

      {error instanceof Error && <Text style={styles.error}>{error.message}</Text>}

      {isLoading ? (
        <SceneGridSkeleton count={8} />
      ) : (
      <FlatList
        {...sceneGridProps(gridColumns)}
        data={items}
        keyExtractor={(s) => s.id}
        // removeClippedSubviews + okno renderowania (windowSize itd.) idą z sceneGridProps
        // — patrz SceneTile. Perf bug-report 5b7ca1e1 (jank/obciążenie przy długim scrollu).
        renderItem={({ item }) => <SceneTile scene={item} />}
        ListHeaderComponent={!debouncedQ && activeCount === 0 ? <ContinueWatchingRail /> : null}
        refreshing={isRefetching}
        onRefresh={refetch}
        onEndReached={() => {
          if (hasNextPage && !isFetchingNextPage) fetchNextPage();
        }}
        onEndReachedThreshold={0.5}
        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 }}
      />
      )}

      <ScenesFilterModal
        visible={filterOpen}
        initial={filter}
        onApply={(next) => {
          setFilter(next);
          setFilterOpen(false);
        }}
        onClose={() => setFilterOpen(false)}
      />
    </View>
  );
}

function ContinueWatchingRail() {
  const client = useClient();
  const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Scenes'>>();
  const { data } = useQuery({
    queryKey: ['watch-recent'],
    queryFn: () => client.listRecentWatch(10),
    staleTime: 30_000,
  });
  const items = data?.items ?? [];
  if (items.length === 0) return null;

  return (
    <View style={styles.rail}>
      <Text style={styles.railLabel}>Continue watching</Text>
      <ScrollView
        horizontal
        showsHorizontalScrollIndicator={false}
        contentContainerStyle={styles.railContent}
      >
        {items.map((entry) => {
          const scene = entry.scene;
          const thumb = scene.playback_sources.find((s) => s.thumbnail_url)?.thumbnail_url;
          const dur = entry.duration_sec || scene.duration_sec || 0;
          const pct = dur > 0 ? Math.min(100, Math.round((entry.position_sec / dur) * 100)) : 0;
          return (
            <Pressable
              key={scene.id}
              style={styles.railItem}
              onPress={() => navigation.navigate('SceneDetail', { id: scene.id })}
            >
              <Thumb url={thumb} style={styles.railThumb} />
              {pct > 0 ? (
                <View style={styles.railProgressBg}>
                  <View style={[styles.railProgressFg, { width: `${pct}%` }]} />
                </View>
              ) : null}
              <Text style={styles.railTitle} numberOfLines={2}>
                {scene.title}
              </Text>
            </Pressable>
          );
        })}
      </ScrollView>
    </View>
  );
}

function FavoritesButton() {
  const client = useClient();
  const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, 'Scenes'>>();
  const { data } = useQuery({
    queryKey: ['favorites'],
    queryFn: () => client.listFavorites(),
    refetchOnWindowFocus: true,
    staleTime: 30_000,
  });
  const newTotal = data?.new_total ?? 0;
  const hasNew = newTotal > 0;

  return (
    <Pressable
      style={[styles.subButton, hasNew && styles.subButtonActive]}
      onPress={() => navigation.navigate('Favorites')}
    >
      <Text style={[styles.subButtonText, hasNew && styles.subButtonTextActive]}>
        ★ {hasNew ? newTotal : ''}
      </Text>
    </Pressable>
  );
}

function SceneRow({ scene }: { scene: SceneOut }) {
  const navigation =
    useNavigation<NativeStackNavigationProp<RootStackParamList, 'Scenes'>>();
  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(() => {});
  };

  // Watched indicator: finished=True dim'uje cały kafelek; pośredni progress (>0,
  // <finished) trzymamy bez dim'a żeby user widział "częściowo obejrzane" ale wraca chętnie.
  const dim = scene.finished === true;

  return (
    <Pressable
      style={[styles.row, dim && styles.rowDimmed]}
      onPress={() => navigation.navigate('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: 16, paddingTop: 16 },
  toolbar: { flexDirection: 'row', gap: 12, marginBottom: 12 },
  search: {
    flex: 1,
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1.5,
    borderRadius: 12,
    color: theme.fg,
    padding: 12,
    fontSize: 16,
  },
  toolbarButton: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1.5,
    borderRadius: 12,
    paddingHorizontal: 16,
    justifyContent: 'center',
  },
  toolbarButtonActive: { borderColor: theme.accent, backgroundColor: `${theme.accent}1A` },
  toolbarButtonText: { color: theme.fg, fontWeight: '700' },
  savedRow: { flexGrow: 0, marginBottom: 12 },
  savedRowContent: { gap: 8, paddingRight: 12 },
  savedChip: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
    borderRadius: 14,
    paddingHorizontal: 12,
    paddingVertical: 6,
    maxWidth: 200,
  },
  savedChipActive: { borderColor: theme.accent, backgroundColor: `${theme.accent}1A` },
  savedChipText: { color: theme.fg, fontSize: 13 },
  savedChipTextActive: { color: theme.accent, fontWeight: '600' },
  subToolbar: { flexDirection: 'row', gap: 8, marginBottom: 16 },
  subButton: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1.5,
    borderRadius: 12,
    paddingHorizontal: 14,
    paddingVertical: 8,
  },
  subButtonActive: {
    backgroundColor: theme.accent,
    borderColor: theme.accent,
    shadowColor: theme.accent,
    shadowOffset: { width: 0, height: 0 },
    shadowOpacity: 0.5,
    shadowRadius: 6,
    elevation: 3,
  },
  subButtonText: { color: theme.accentSecondary, fontSize: 13, fontWeight: '700' },
  subButtonTextActive: { color: theme.fg },
  row: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
    borderRadius: 12,
    padding: 12,
    marginBottom: 12,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.2,
    shadowRadius: 4,
    elevation: 3,
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
  },
  rowDimmed: { opacity: 0.45 },
  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' },
  thumbnail: {
    width: 100,
    height: 56,
    borderRadius: 8,
    backgroundColor: theme.border,
  },
  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: 32, fontSize: 16 },
  error: { color: theme.bad, padding: 16 },

  chip: {
    borderColor: theme.border,
    borderWidth: 1.5,
    borderRadius: 18,
    paddingHorizontal: 14,
    paddingVertical: 7,
    backgroundColor: theme.card,
  },
  chipActive: {
    borderColor: theme.accent,
    backgroundColor: `${theme.accent}26`,
    shadowColor: theme.accent,
    shadowOffset: { width: 0, height: 0 },
    shadowOpacity: 0.4,
    shadowRadius: 6,
    elevation: 2,
  },
  chipText: { color: theme.muted, fontSize: 13, fontWeight: '700' },
  chipTextActive: { color: theme.accent },

  // Continue watching rail
  rail: { marginBottom: 16 },
  railLabel: {
    color: theme.muted,
    fontSize: 11,
    textTransform: 'uppercase',
    letterSpacing: 1.2,
    fontWeight: '700',
    marginBottom: 8,
    paddingLeft: 2,
  },
  railContent: { gap: 10, paddingRight: 8 },
  railItem: { width: 160 },
  railThumb: {
    width: 160,
    height: 90,
    borderRadius: 8,
    backgroundColor: theme.bgElevated,
  },
  railThumbPlaceholder: {
    borderColor: theme.border,
    borderWidth: 1,
  },
  railProgressBg: {
    height: 3,
    backgroundColor: theme.bgElevated,
    borderRadius: 2,
    marginTop: 4,
    overflow: 'hidden',
  },
  railProgressFg: { height: 3, backgroundColor: theme.accent },
  railTitle: {
    color: theme.fg,
    fontSize: 12,
    fontWeight: '600',
    marginTop: 6,
    lineHeight: 15,
  },

  // 2-col grid wrapper — SceneTile sam ma styling tile/thumb/etc, tu tylko
  // odstęp między tilami w wierszu i pod wierszem.
  gridRow: { gap: 10, marginBottom: 14 },
});
