import React, {useEffect, useState} from 'react';
import {
  ActivityIndicator,
  FlatList,
  Image,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  TextInput,
  TouchableOpacity as RNTouchableOpacity,
  View,
} from 'react-native';
import {TVTouchable as TouchableOpacity} from '../components/common/TVTouchable';
import {useNavigation} from '@react-navigation/native';
import axios from 'axios';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import {addonManager} from '../api/addonManager';
import {useAddonStore} from '../store/addonStore';
import {colors, typography} from '../utils/colors';
import {IS_TV, TV_FOCUS_COLOR} from '../utils/tv';

// ── Curated built-in catalog ──────────────────────────────────────────────────
// These are always shown even when offline. Install does a live manifest fetch.
interface AddonEntry {
  transportUrl: string;
  id: string;
  name: string;
  description: string;
  types: string[];
  logo?: string;
  featured?: boolean;
}

const CURATED: AddonEntry[] = [
  // ── Stream providers (find content to watch) ──
  {
    transportUrl: 'https://torrentio.strem.fun/manifest.json',
    id: '_curated_torrentio',
    name: 'Torrentio',
    description:
      'Stream from YTS, EZTV, The Pirate Bay and 10+ torrent sites. Works great with Real-Debrid and AllDebrid.',
    types: ['movie', 'series'],
    logo: 'https://torrentio.strem.fun/images/icon.png',
    featured: true,
  },
  {
    transportUrl: 'https://comet.feels.legal/manifest.json',
    id: '_curated_comet',
    name: 'Comet',
    description:
      'Fast torrent/debrid search addon. Alternative to Torrentio with great debrid support.',
    types: ['movie', 'series'],
    logo: 'https://raw.githubusercontent.com/g0ldyy/comet/refs/heads/main/comet/assets/icon.png',
    featured: true,
  },
  {
    transportUrl: 'https://mediafusion.elfhosted.com/manifest.json',
    id: '_curated_mediafusion',
    name: 'MediaFusion',
    description:
      'Open-source streaming for movies, series and live TV. Supports multiple torrent and debrid sources.',
    types: ['movie', 'series'],
    logo: 'https://mediafusion.elfhosted.com/static/images/MediaFusion%20Logo.png',
    featured: true,
  },
  {
    transportUrl: 'https://5a0d1888fa64-orion.baby-beamup.club/manifest.json',
    id: '_curated_orion',
    name: 'Orion',
    description:
      'Orion-indexed torrent, usenet and hoster links. Cached links can be used to stream instantly via debrid.',
    types: ['movie', 'series'],
    featured: false,
  },
  {
    transportUrl: 'https://ea627ddf0ee7-easynews.baby-beamup.club/manifest.json',
    id: '_curated_easynews',
    name: 'Easynews',
    description:
      'Stream content from Easynews usenet provider. Requires an Easynews subscription.',
    types: ['movie', 'series'],
    logo: 'https://pbs.twimg.com/profile_images/479627852757733376/8v9zH7Yo_400x400.jpeg',
    featured: false,
  },
  {
    transportUrl: 'https://b89262c192b0-stremio-consumet-addon.baby-beamup.club/manifest.json',
    id: '_curated_consumet',
    name: 'Consumet',
    description:
      'Free anime and asian movie streams from the Consumet API. No debrid required.',
    types: ['movie', 'series'],
    featured: false,
  },
  {
    transportUrl: 'https://archivio.elfhosted.com/manifest.json',
    id: '_curated_archivio',
    name: 'Archivio',
    description:
      'Watch free public domain movies from the Internet Archive, directly in the app.',
    types: ['movie', 'series'],
    logo: 'https://docs.elfhosted.com/images/logo.svg',
    featured: false,
  },

  // ── Catalogs (discover content) ──
  {
    transportUrl: 'https://v3-cinemeta.strem.io/manifest.json',
    id: '_curated_cinemeta',
    name: 'Cinemeta',
    description:
      'The official catalog of movies & series, powered by IMDB with trailers, ratings and cast.',
    types: ['movie', 'series'],
    featured: true,
  },
  {
    transportUrl: 'https://94c8cb9f702d-tmdb-addon.baby-beamup.club/manifest.json',
    id: '_curated_tmdb',
    name: 'TMDB Addon',
    description:
      'Rich metadata for movies and TV shows from The Movie Database with high-res posters, trailers and multi-language support.',
    types: ['movie', 'series'],
    logo: 'https://94c8cb9f702d-tmdb-addon.baby-beamup.club/logo.png',
    featured: false,
  },
  {
    transportUrl: 'https://7a82163c306e-stremio-netflix-catalog-addon.baby-beamup.club/manifest.json',
    id: '_curated_streaming_catalogs',
    name: 'Streaming Catalogs',
    description:
      'Browse trending movies and series on Netflix, HBO Max, Disney+, Apple TV+, Amazon Prime and more.',
    types: ['movie', 'series'],
    logo: 'https://play-lh.googleusercontent.com/TBRwjS_qfJCSj1m7zZB93FnpJM5fSpMA_wUlFDLxWAb45T9RmwBvQd5cWR5viJJOhkI',
    featured: false,
  },
  {
    transportUrl: 'https://7a82163c306d-imdb-catalogs.baby-beamup.club/manifest.json',
    id: '_curated_imdb',
    name: 'IMDB Catalogs',
    description:
      'Browse IMDB Top 250, Most Popular and genre-filtered lists directly in your library.',
    types: ['movie', 'series'],
    featured: false,
  },
  {
    transportUrl: 'https://1fe84bc728af-stremio-mdblist.baby-beamup.club/manifest.json',
    id: '_curated_mdblist',
    name: 'MDBList',
    description:
      'Custom curated lists from MDBList with rating posters. Discover trending, popular and top-rated content.',
    types: ['movie', 'series'],
    logo: 'https://mdblist.com/static/mdblist.png',
    featured: false,
  },
  {
    transportUrl: 'https://1fe84bc728af-rpdb.baby-beamup.club/manifest.json',
    id: '_curated_rpdb',
    name: 'RPDB Catalogs',
    description:
      'Track new, popular and best releases with ratings from all popular rating websites overlaid on posters.',
    types: ['movie', 'series'],
    logo: 'https://1fe84bc728af-rpdb.baby-beamup.club/addon-logo.png',
    featured: false,
  },
  {
    transportUrl: 'https://stremio-ratings-v3-production.up.railway.app/manifest.json',
    id: '_curated_imdb_ratings',
    name: 'IMDb Ratings',
    description:
      'Shows IMDb ratings for movies and individual TV episodes with a configurable format. Works with anime.',
    types: ['movie', 'series'],
    featured: false,
  },

  // ── Anime ──
  {
    transportUrl: 'https://anime-kitsu.strem.fun/manifest.json',
    id: '_curated_kitsu',
    name: 'Anime Kitsu',
    description:
      'Complete anime catalog with episode tracking, ratings and metadata powered by Kitsu.io.',
    types: ['series'],
    logo: 'https://i.imgur.com/7N6XGoO.png',
    featured: false,
  },
  {
    transportUrl: 'https://1fe84bc728af-stremio-anime-catalogs.baby-beamup.club/manifest.json',
    id: '_curated_anime_catalogs',
    name: 'Anime Catalogs',
    description:
      'Anime catalogs from MyAnimeList, AniDB, AniList, Kitsu, aniSearch, LiveChart and Notify.Moe. Filter by dubbed.',
    types: ['series', 'movie'],
    logo: 'https://1fe84bc728af-stremio-anime-catalogs.baby-beamup.club/addon-logo.png',
    featured: false,
  },
  {
    transportUrl: 'https://k-drama-kq1pjckm.on-forge.com/manifest.json',
    id: '_curated_asian_tv',
    name: 'Asian-TV',
    description:
      'Discover Asian TV shows, K-dramas and movies. Catalog-only — use with your streaming addons.',
    types: ['movie', 'series'],
    logo: 'https://k-drama-kq1pjckm.on-forge.com/logo.png',
    featured: false,
  },

  // ── Subtitles ──
  {
    transportUrl: 'https://opensubtitles-v3.strem.io/manifest.json',
    id: '_curated_opensubtitles',
    name: 'OpenSubtitles v3',
    description:
      'Subtitles for movies and series in 100+ languages from OpenSubtitles.org.',
    types: ['subtitles'],
    featured: false,
  },
  {
    transportUrl: 'https://3b4bbf5252c4-aio-streaming.baby-beamup.club/manifest.json',
    id: '_curated_aio_subtitle',
    name: 'AIO Subtitle',
    description:
      'Multi-source subtitles from Subscene, OpenSubtitles, and Kitsunekko with auto-translation support.',
    types: ['subtitles'],
    logo: 'https://3b4bbf5252c4-aio-streaming.baby-beamup.club/assets/AI-sub.png',
    featured: false,
  },
  {
    transportUrl: 'https://stream-vault.ai/addons/hebrew-subs/manifest.json',
    id: '_curated_hebrew_subs',
    name: 'Hebrew Subs',
    description:
      'Manually synced Hebrew subtitles for select movies. Timed for WEB-DL releases.',
    types: ['subtitles'],
    featured: false,
  },

  // ── Utility ──
  {
    transportUrl: 'https://stremio-cast-search.onrender.com/manifest.json',
    id: '_curated_cast_search',
    name: 'Cast Search',
    description:
      'Makes cast members clickable and searchable — great for discovering more films by your favorite actors.',
    types: ['movie', 'series'],
    featured: false,
  },
  {
    transportUrl: 'https://68d69db7dc40-debrid-search.baby-beamup.club/manifest.json',
    id: '_curated_debrid_search',
    name: 'Debrid Search',
    description:
      'Search downloads and torrents already cached in your debrid cloud. Requires Real-Debrid or AllDebrid.',
    types: ['movie', 'series'],
    logo: 'https://img.icons8.com/fluency/256/search-in-cloud.png',
    featured: false,
  },
];

// Live community catalog endpoints to try (in order)
const LIVE_CATALOG_URLS = [
  'https://api.strem.io/addonscollection.json',
  'https://stremio-addons.com/catalog.json',
];

type TypeFilter = 'all' | 'movie' | 'series' | 'subtitles';

interface LiveAddon {
  transportUrl: string;
  manifest: {
    id: string;
    name: string;
    description?: string;
    types?: string[];
    logo?: string;
    version?: string;
  };
}

export default function AddonBrowseScreen() {
  const navigation = useNavigation();
  const {addons, refresh} = useAddonStore();
  const [query, setQuery] = useState('');
  const [typeFilter, setTypeFilter] = useState<TypeFilter>('all');
  const [liveAddons, setLiveAddons] = useState<LiveAddon[]>([]);
  const [loadingLive, setLoadingLive] = useState(true);
  const [installing, setInstalling] = useState<string | null>(null);

  // Try to fetch live community catalog in background
  useEffect(() => {
    let cancelled = false;
    setLoadingLive(true);

    const tryFetch = async () => {
      for (const url of LIVE_CATALOG_URLS) {
        try {
          const res = await axios.get(url, {
            timeout: 8000,
            headers: {'User-Agent': 'Stremio/4.4 (Android)'},
          });
          if (cancelled) return;
          let items: LiveAddon[] = [];
          const data = res.data;
          if (Array.isArray(data)) {
            items = data.filter(
              (d: any) => d?.manifest?.id && d?.transportUrl,
            ) as LiveAddon[];
          } else if (Array.isArray(data?.addons)) {
            items = (data.addons as any[]).filter(
              d => d?.manifest?.id && d?.transportUrl,
            ) as LiveAddon[];
          }
          if (items.length > 0) {
            setLiveAddons(items);
            break;
          }
        } catch {
          // try next URL or give up
        }
      }
      if (!cancelled) setLoadingLive(false);
    };

    tryFetch();
    return () => {
      cancelled = true;
    };
  }, []);

  const isInstalled = (id: string) =>
    addons.some(a => a.manifest.id === id);

  const isInstalledByUrl = (url: string) => {
    const normalized = url.replace(/\/manifest\.json$/, '');
    return addons.some(a => {
      const aUrl = a.transportUrl.replace(/\/manifest\.json$/, '');
      return aUrl === normalized;
    });
  };

  const handleInstallCurated = async (entry: AddonEntry) => {
    if (isInstalledByUrl(entry.transportUrl)) return;
    setInstalling(entry.id);
    try {
      await addonManager.installAddon(entry.transportUrl);
      refresh();
    } catch {
      // silently fail
    } finally {
      setInstalling(null);
    }
  };

  const handleInstallLive = async (addon: LiveAddon) => {
    if (isInstalled(addon.manifest.id)) return;
    setInstalling(addon.manifest.id);
    try {
      await addonManager.saveAddon(addon.transportUrl, addon.manifest as any);
      refresh();
    } catch {
      // silently fail
    } finally {
      setInstalling(null);
    }
  };

  // Filter curated list
  const filteredCurated = CURATED.filter(a => {
    if (typeFilter !== 'all' && !a.types.includes(typeFilter)) return false;
    if (!query.trim()) return true;
    const q = query.toLowerCase();
    return a.name.toLowerCase().includes(q) || a.description.toLowerCase().includes(q);
  });

  // Build live addons list — exclude ones already in curated or installed
  const curatedUrls = new Set(
    CURATED.map(c => c.transportUrl.replace(/\/manifest\.json$/, '')),
  );
  const filteredLive = liveAddons.filter(a => {
    const baseUrl = a.transportUrl.replace(/\/manifest\.json$/, '');
    if (curatedUrls.has(baseUrl)) return false;
    if (typeFilter !== 'all') {
      if (!a.manifest.types?.includes(typeFilter)) return false;
    }
    if (query.trim()) {
      const q = query.toLowerCase();
      return (
        a.manifest.name.toLowerCase().includes(q) ||
        (a.manifest.description ?? '').toLowerCase().includes(q)
      );
    }
    return true;
  });

  // Build combined items list
  const allItems = [
    ...filteredCurated.map(c => ({kind: 'curated' as const, data: c})),
    ...filteredLive.map(l => ({kind: 'live' as const, data: l})),
  ];

  // ── TV layout: single ScrollView, no nested scrollables ──
  if (IS_TV) {
    return (
      <View style={styles.container}>
        <StatusBar barStyle="light-content" />
        <ScrollView showsVerticalScrollIndicator={false}>
          {/* Header */}
          <View style={styles.header}>
            <RNTouchableOpacity
              onPress={() => navigation.goBack()}
              style={styles.backBtn}
              focusable={true}
              hasTVPreferredFocus={true}>
              <Icon name="arrow-left" size={24} color={colors.textPrimary} />
            </RNTouchableOpacity>
            <View style={styles.headerText}>
              <Text style={styles.headerTitle}>Browse Addons</Text>
              <Text style={styles.headerSub}>
                {CURATED.length + filteredLive.length} available
                {loadingLive ? ' · loading more…' : ''}
              </Text>
            </View>
          </View>

          {/* Type filter — plain row, no ScrollView */}
          <View style={styles.tvFilterRow}>
            {(['all', 'movie', 'series', 'subtitles'] as TypeFilter[]).map(f => (
              <RNTouchableOpacity
                key={f}
                style={[styles.chip, typeFilter === f && styles.chipActive]}
                onPress={() => setTypeFilter(f)}
                focusable={true}>
                <Text style={[styles.chipText, typeFilter === f && styles.chipTextActive]}>
                  {f === 'all' ? 'All' : f === 'movie' ? 'Movies' : f === 'series' ? 'Series' : 'Subtitles'}
                </Text>
              </RNTouchableOpacity>
            ))}
          </View>

          {/* Addon cards */}
          <View style={styles.list}>
            {allItems.length === 0 ? (
              <View style={styles.empty}>
                <Icon name="magnify-remove-outline" size={48} color={colors.textMuted} />
                <Text style={styles.emptyText}>No addons match your search.</Text>
              </View>
            ) : (
              allItems.map((item, idx) => {
                if (item.kind === 'curated') {
                  const c = item.data;
                  const installed = isInstalledByUrl(c.transportUrl);
                  const isInstalling = installing === c.id;
                  return (
                    <TVAddonCard
                      key={c.id}
                      logo={c.logo}
                      name={c.name}
                      description={c.description}
                      types={c.types}
                      version={undefined}
                      installed={installed}
                      installing={isInstalling}
                      featured={c.featured}
                      onInstall={() => handleInstallCurated(c)}
                      isFirst={idx === 0}
                    />
                  );
                } else {
                  const l = item.data;
                  const installed = isInstalled(l.manifest.id);
                  const isInstalling = installing === l.manifest.id;
                  return (
                    <TVAddonCard
                      key={l.manifest.id + idx}
                      logo={l.manifest.logo}
                      name={l.manifest.name}
                      description={l.manifest.description}
                      types={l.manifest.types ?? []}
                      version={l.manifest.version}
                      installed={installed}
                      installing={isInstalling}
                      featured={false}
                      onInstall={() => handleInstallLive(l)}
                      isFirst={false}
                    />
                  );
                }
              })
            )}
            {loadingLive && (
              <View style={styles.liveLoading}>
                <ActivityIndicator size="small" color={colors.primary} />
                <Text style={styles.liveLoadingText}>Loading community catalog…</Text>
              </View>
            )}
          </View>
        </ScrollView>
      </View>
    );
  }

  // ── Phone layout: original with FlatList ──
  return (
    <View style={styles.container}>
      <StatusBar barStyle="light-content" />

      {/* Header */}
      <View style={styles.header}>
        <TouchableOpacity onPress={() => navigation.goBack()} style={styles.backBtn}>
          <Icon name="arrow-left" size={24} color={colors.textPrimary} />
        </TouchableOpacity>
        <View style={styles.headerText}>
          <Text style={styles.headerTitle}>Browse Addons</Text>
          <Text style={styles.headerSub}>
            {CURATED.length + filteredLive.length} available
            {loadingLive ? ' · loading more…' : ''}
          </Text>
        </View>
      </View>

      {/* Search */}
      <View style={styles.searchRow}>
        <Icon name="magnify" size={18} color={colors.textMuted} style={{marginRight: 6}} />
        <TextInput
          style={styles.searchInput}
          placeholder="Search addons..."
          placeholderTextColor={colors.textMuted}
          value={query}
          onChangeText={setQuery}
          autoCapitalize="none"
          autoCorrect={false}
        />
        {query.length > 0 && (
          <TouchableOpacity onPress={() => setQuery('')}>
            <Icon name="close-circle" size={16} color={colors.textMuted} />
          </TouchableOpacity>
        )}
      </View>

      {/* Type filter */}
      <ScrollView
        horizontal
        showsHorizontalScrollIndicator={false}
        contentContainerStyle={styles.filterRow}>
        {(['all', 'movie', 'series', 'subtitles'] as TypeFilter[]).map(f => (
          <TouchableOpacity
            key={f}
            style={[styles.chip, typeFilter === f && styles.chipActive]}
            onPress={() => setTypeFilter(f)}>
            <Text style={[styles.chipText, typeFilter === f && styles.chipTextActive]}>
              {f === 'all' ? 'All' : f === 'movie' ? 'Movies' : f === 'series' ? 'Series' : 'Subtitles'}
            </Text>
          </TouchableOpacity>
        ))}
      </ScrollView>

      <FlatList
        data={allItems}
        keyExtractor={(item, i) =>
          item.kind === 'curated' ? item.data.id : item.data.manifest.id + i
        }
        contentContainerStyle={styles.list}
        showsVerticalScrollIndicator={false}
        ListEmptyComponent={
          <View style={styles.empty}>
            <Icon name="magnify-remove-outline" size={48} color={colors.textMuted} />
            <Text style={styles.emptyText}>No addons match your search.</Text>
          </View>
        }
        ListFooterComponent={
          loadingLive ? (
            <View style={styles.liveLoading}>
              <ActivityIndicator size="small" color={colors.primary} />
              <Text style={styles.liveLoadingText}>Loading community catalog…</Text>
            </View>
          ) : null
        }
        renderItem={({item}) => {
          if (item.kind === 'curated') {
            const c = item.data;
            const installed = isInstalledByUrl(c.transportUrl);
            const isInstalling = installing === c.id;
            return (
              <AddonCard
                logo={c.logo}
                name={c.name}
                description={c.description}
                types={c.types}
                version={undefined}
                installed={installed}
                installing={isInstalling}
                featured={c.featured}
                onInstall={() => handleInstallCurated(c)}
              />
            );
          } else {
            const l = item.data;
            const installed = isInstalled(l.manifest.id);
            const isInstalling = installing === l.manifest.id;
            return (
              <AddonCard
                logo={l.manifest.logo}
                name={l.manifest.name}
                description={l.manifest.description}
                types={l.manifest.types ?? []}
                version={l.manifest.version}
                installed={installed}
                installing={isInstalling}
                featured={false}
                onInstall={() => handleInstallLive(l)}
              />
            );
          }
        }}
      />
    </View>
  );
}

// ── TV-specific card: entire card is a single RN TouchableOpacity ────────────
// Uses RNTouchableOpacity directly (not TVTouchable) so Fire TV's native focus
// system handles it without relying on JS focus events.
interface TVAddonCardProps {
  logo?: string;
  name: string;
  description?: string;
  types: string[];
  version?: string;
  installed: boolean;
  installing: boolean;
  featured?: boolean;
  onInstall: () => void;
  isFirst: boolean;
}

function TVAddonCard({
  logo,
  name,
  description,
  types,
  version,
  installed,
  installing,
  featured,
  onInstall,
  isFirst,
}: TVAddonCardProps) {
  return (
    <RNTouchableOpacity
      style={[cardStyles.card, featured && cardStyles.cardFeatured]}
      onPress={installed || installing ? undefined : onInstall}
      activeOpacity={0.8}
      focusable={true}
      disabled={installed || installing}>
      {featured && (
        <View style={cardStyles.featuredBadge}>
          <Text style={cardStyles.featuredText}>POPULAR</Text>
        </View>
      )}
      <View style={cardStyles.row}>
        {logo ? (
          <Image style={cardStyles.logo} source={{uri: logo}} resizeMode="contain" />
        ) : (
          <View style={[cardStyles.logo, cardStyles.logoPlaceholder]}>
            <Text style={cardStyles.logoLetter}>{name[0]}</Text>
          </View>
        )}
        <View style={cardStyles.info}>
          <Text style={cardStyles.name} numberOfLines={1}>{name}</Text>
          <View style={cardStyles.badges}>
            {types.slice(0, 4).map(t => (
              <View key={t} style={cardStyles.badge}>
                <Text style={cardStyles.badgeText}>{t}</Text>
              </View>
            ))}
          </View>
        </View>
        <View style={[cardStyles.installBtn, installed && cardStyles.installedBtn]}>
          {installing ? (
            <ActivityIndicator size="small" color={colors.textPrimary} />
          ) : installed ? (
            <>
              <Icon name="check" size={13} color={colors.success} />
              <Text style={[cardStyles.btnText, {color: colors.success}]}>Installed</Text>
            </>
          ) : (
            <>
              <Icon name="plus" size={13} color="#fff" />
              <Text style={cardStyles.btnText}>Install</Text>
            </>
          )}
        </View>
      </View>
      {description ? (
        <Text style={cardStyles.desc} numberOfLines={2}>{description}</Text>
      ) : null}
      {version ? <Text style={cardStyles.version}>v{version}</Text> : null}
    </RNTouchableOpacity>
  );
}

// ── Phone card component ─────────────────────────────────────────────────────
interface AddonCardProps {
  logo?: string;
  name: string;
  description?: string;
  types: string[];
  version?: string;
  installed: boolean;
  installing: boolean;
  featured?: boolean;
  onInstall: () => void;
}

function AddonCard({
  logo,
  name,
  description,
  types,
  version,
  installed,
  installing,
  featured,
  onInstall,
}: AddonCardProps) {
  return (
    <View style={[cardStyles.card, featured && cardStyles.cardFeatured]}>
      {featured && (
        <View style={cardStyles.featuredBadge}>
          <Text style={cardStyles.featuredText}>POPULAR</Text>
        </View>
      )}
      <View style={cardStyles.row}>
        {logo ? (
          <Image style={cardStyles.logo} source={{uri: logo}} resizeMode="contain" />
        ) : (
          <View style={[cardStyles.logo, cardStyles.logoPlaceholder]}>
            <Text style={cardStyles.logoLetter}>{name[0]}</Text>
          </View>
        )}
        <View style={cardStyles.info}>
          <Text style={cardStyles.name} numberOfLines={1}>{name}</Text>
          <View style={cardStyles.badges}>
            {types.slice(0, 4).map(t => (
              <View key={t} style={cardStyles.badge}>
                <Text style={cardStyles.badgeText}>{t}</Text>
              </View>
            ))}
          </View>
        </View>
        <TouchableOpacity
          style={[cardStyles.installBtn, installed && cardStyles.installedBtn]}
          onPress={onInstall}
          disabled={installed || installing}>
          {installing ? (
            <ActivityIndicator size="small" color={colors.textPrimary} />
          ) : installed ? (
            <>
              <Icon name="check" size={13} color={colors.success} />
              <Text style={[cardStyles.btnText, {color: colors.success}]}>Installed</Text>
            </>
          ) : (
            <>
              <Icon name="plus" size={13} color="#fff" />
              <Text style={cardStyles.btnText}>Install</Text>
            </>
          )}
        </TouchableOpacity>
      </View>
      {description ? (
        <Text style={cardStyles.desc} numberOfLines={2}>{description}</Text>
      ) : null}
      {version ? <Text style={cardStyles.version}>v{version}</Text> : null}
    </View>
  );
}

// ── Styles ────────────────────────────────────────────────────────────────────
const styles = StyleSheet.create({
  container: {flex: 1, backgroundColor: colors.background},
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingHorizontal: 16,
    paddingTop: 48,
    paddingBottom: 12,
    gap: 12,
  },
  backBtn: {padding: 4},
  headerText: {flex: 1},
  headerTitle: {...typography.title, color: colors.textPrimary},
  headerSub: {fontSize: 11, color: colors.textMuted, marginTop: 2},
  searchRow: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: colors.surface,
    borderRadius: 12,
    marginHorizontal: 16,
    marginBottom: 10,
    paddingHorizontal: 10,
    borderWidth: 1,
    borderColor: colors.border,
    height: 44,
  },
  searchInput: {
    flex: 1,
    color: colors.textPrimary,
    fontSize: 14,
    paddingVertical: 0,
  },
  filterRow: {
    paddingHorizontal: 16,
    paddingBottom: 12,
    gap: 8,
  },
  tvFilterRow: {
    flexDirection: 'row',
    paddingHorizontal: 16,
    paddingBottom: 12,
    gap: 8,
  },
  chip: {
    paddingHorizontal: 14,
    paddingVertical: 6,
    borderRadius: 16,
    backgroundColor: colors.surfaceLight,
    marginRight: 4,
  },
  chipActive: {backgroundColor: colors.primary},
  chipText: {fontSize: 12, color: colors.textSecondary, fontWeight: '500'},
  chipTextActive: {color: '#fff', fontWeight: '700'},
  list: {paddingHorizontal: 16, paddingBottom: 24},
  empty: {
    alignItems: 'center',
    paddingTop: 60,
    gap: 12,
  },
  emptyText: {...typography.body, color: colors.textMuted, textAlign: 'center'},
  liveLoading: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    paddingVertical: 16,
    gap: 8,
  },
  liveLoadingText: {fontSize: 12, color: colors.textMuted},
});

const cardStyles = StyleSheet.create({
  card: {
    backgroundColor: colors.surface,
    borderRadius: 14,
    padding: 14,
    marginBottom: 10,
    borderWidth: 0.5,
    borderColor: colors.border,
  },
  cardFeatured: {
    borderColor: colors.primary + '50',
    backgroundColor: colors.surface,
  },
  featuredBadge: {
    position: 'absolute',
    top: 12,
    right: 12,
    backgroundColor: colors.primary + '28',
    paddingHorizontal: 6,
    paddingVertical: 2,
    borderRadius: 4,
    borderWidth: 0.5,
    borderColor: colors.primary + '60',
  },
  featuredText: {
    fontSize: 8,
    fontWeight: '800',
    color: colors.primaryLight,
    letterSpacing: 0.8,
  },
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
    marginBottom: 6,
    paddingRight: 80, // space for featured badge
  },
  logo: {width: 44, height: 44, borderRadius: 10},
  logoPlaceholder: {
    backgroundColor: colors.primary + '30',
    justifyContent: 'center',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: colors.primary + '55',
  },
  logoLetter: {color: colors.primaryLight, fontWeight: '800', fontSize: 20},
  info: {flex: 1},
  name: {
    fontSize: 15,
    fontWeight: '700',
    color: colors.textPrimary,
    marginBottom: 4,
  },
  badges: {flexDirection: 'row', gap: 4, flexWrap: 'wrap'},
  badge: {
    backgroundColor: colors.surfaceHighlight,
    paddingHorizontal: 6,
    paddingVertical: 2,
    borderRadius: 4,
    borderWidth: 0.5,
    borderColor: colors.border,
  },
  badgeText: {
    fontSize: 9,
    fontWeight: '700',
    color: colors.textMuted,
    textTransform: 'uppercase',
    letterSpacing: 0.3,
  },
  installBtn: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 4,
    backgroundColor: colors.surfaceHighlight,
    paddingHorizontal: 11,
    paddingVertical: 8,
    borderRadius: 8,
    minWidth: 80,
    justifyContent: 'center',
    borderWidth: 1,
    borderColor: colors.primary,
  },
  installedBtn: {
    backgroundColor: colors.success + '15',
    borderWidth: 1,
    borderColor: colors.success + '45',
  },
  btnText: {fontSize: 12, fontWeight: '700', color: '#fff'},
  desc: {fontSize: 12, color: colors.textSecondary, lineHeight: 17, marginBottom: 4},
  version: {fontSize: 10, color: colors.textMuted},
});
