import { memo, useState, useCallback, useRef, useEffect } from 'react';
import { View, Text, Pressable, Animated, TouchableOpacity } from 'react-native';
import { Image } from 'expo-image';
import { Ionicons } from '@expo/vector-icons';
import { useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { useRouter } from 'expo-router';
import i18n from '../lib/i18n';
import { apiFetch } from '../lib/api';
import { formatRelativeTime } from '../lib/formatTime';
import { useCommunityStore, type CommunityPost } from '../stores/community';
import { RiveAvatar } from './RiveAvatar';
import { HeroShieldCheck } from './HeroShieldCheck';
import { useColors } from '../lib/theme';
import { UserAvatar } from './UserAvatar';

/**
 * Domain-Approval-Posts werden vom Backend in 4 Sprachen parallel via Groq
 * generiert und als JSON-encoded `{de:'...',en:'...',fr:'...',ar:'...'}` im
 * `content`-Feld gespeichert. Diese Helper parsed das + pickt die aktuelle
 * App-Locale; fällt auf DE zurück wenn locale fehlt; gibt plain content
 * zurück wenn der content kein JSON ist (Legacy-Posts, User-Posts, Reposts).
 */
function resolveLocalizedJsonContent(raw: string | null | undefined, currentLang: string): string {
  if (!raw) return '';
  // Quick-Reject: wenn nicht mit '{' anfängt, ist's mit Sicherheit kein JSON.
  // Vermeidet JSON.parse-Overhead für 99.9% der Posts (normale Text-Posts).
  if (raw[0] !== '{') return raw;
  try {
    const parsed = JSON.parse(raw);
    if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
      const langs = ['de', 'en', 'fr', 'ar'];
      const hasLocaleKey = Object.keys(parsed).some((k) => langs.includes(k));
      if (hasLocaleKey) {
        return parsed[currentLang] ?? parsed.de ?? parsed.en ?? raw;
      }
    }
  } catch {
    // not JSON, fall through
  }
  return raw;
}

type Props = {
  post: CommunityPost;
  onCommentPress: (postId: string) => void;
};

function PostCardImpl({ post, onCommentPress }: Props) {
  const { t } = useTranslation();
  const colors = useColors();
  const queryClient = useQueryClient();
  const router = useRouter();
  // Granular selectors — subscribing to the whole store would re-render every
  // PostCard whenever any user likes any post (optimisticLikes mutates).
  const applyOptimisticLike = useCommunityStore((s) => s.applyOptimisticLike);
  const revertOptimisticLike = useCommunityStore((s) => s.revertOptimisticLike);
  const clearOptimisticLike = useCommunityStore((s) => s.clearOptimisticLike);

  const [localLike, setLocalLike] = useState<'like' | null>(post.userLike === 'like' ? 'like' : null);
  const [localCount, setLocalCount] = useState(post.likesCount);
  const [isLiking, setIsLiking] = useState(false);

  // Foreign-like count sync. When useCommunityRealtime patches the React-Query
  // cache (UPDATE on community_posts → likes_count changed), the prop updates
  // but localCount is seeded once via useState and stays stale. Sync it.
  //
  // The wasLikingRef trick: when isLiking goes true → false (own action just
  // settled), the prop hasn't been patched yet by the realtime broadcast (it
  // arrives ~100-300ms later as Supabase relays our own DB update back to us).
  // We skip the FIRST useEffect run after the transition so we don't overwrite
  // the value handleLike just set with the still-stale prop. The next prop
  // change (= cache patch arriving) re-fires the effect and syncs correctly.
  // For pure foreign likes (no own action in flight) the first run goes
  // through directly.
  const wasLikingRef = useRef(false);
  useEffect(() => {
    if (isLiking) {
      wasLikingRef.current = true;
      return;
    }
    if (wasLikingRef.current) {
      wasLikingRef.current = false;
      return;
    }
    setLocalCount(post.likesCount);
  }, [post.likesCount, isLiking]);

  // Heart-Pop Animation — Insta-Style: quick scale-up + spring-bounce back
  const heartScale = useRef(new Animated.Value(1)).current;
  const triggerHeartPop = useCallback(() => {
    heartScale.setValue(1);
    Animated.sequence([
      Animated.timing(heartScale, {
        toValue: 1.4,
        duration: 120,
        useNativeDriver: true,
      }),
      Animated.spring(heartScale, {
        toValue: 1,
        friction: 4,
        tension: 80,
        useNativeDriver: true,
      }),
    ]).start();
  }, [heartScale]);

  const displayAuthor = post.repostOf ? post.repostOf.author : post.author;
  const rawContent = post.repostOf ? post.repostOf.content : post.content;
  // i18n-aware content. Drei Pfade in Prioritäts-Reihenfolge:
  //   1. post.i18nKey gesetzt → t(`lyra_posts.${id}`) (Catalog-Lyra-Posts)
  //   2. content ist JSON-encoded mit Locale-Keys → pick current locale
  //      (Domain-Approval-Posts, Server-generiert 4x parallel)
  //   3. plain content (Legacy-Posts, Comments, User-Posts) → unverändert
  const i18nKey = post.repostOf ? undefined : post.i18nKey;
  const currentLang = (i18n.language ?? 'de').slice(0, 2);
  const displayContent = i18nKey
    ? t(`lyra_posts.${i18nKey}`)
    : resolveLocalizedJsonContent(rawContent, currentLang);
  const displayImage = post.repostOf ? post.repostOf.imageUrl : post.imageUrl;

  // Image aspect-ratio: ermittelt aus onLoad event.source.{width,height}.
  // Fallback während loading = 1.78 (16:9). Clamp 0.6..1.78 verhindert,
  // dass hyper-portrait-Bilder (9:21) den ganzen Screen einnehmen.
  const [imageAspectRatio, setImageAspectRatio] = useState<number | null>(null);
  // Reset bei post-change (FlatList-Recycling).
  useEffect(() => {
    setImageAspectRatio(null);
  }, [displayImage]);

  const authorLabel = post.isAnonymous || !displayAuthor.id ? t('community.anonymous_label') : displayAuthor.nickname;

  // Lyra bot posts use the RiveAvatar (sm = 40px circle). All other bots and
  // regular users use the image/initials fallback path.
  const isLyraPost = post.isBot && post.botType === 'lyra';

  const avatarUserId = !post.isAnonymous && !isLyraPost ? displayAuthor.id ?? null : null;
  const avatarId = !post.isAnonymous && !isLyraPost ? displayAuthor.avatar ?? null : null;

  // domain_approved: extract domain name from Google favicon URL stored in imageUrl
  const approvedDomain = (() => {
    if (post.category !== 'domain_approved' || !displayImage) return null;
    try { return new URL(displayImage).searchParams.get('domain'); } catch { return null; }
  })();

  // domain_vote vote action — delegate up via apiFetch, no local store mutation needed
  // (realtime hook invalidates query on submission UPDATE)
  const [voting, setVoting] = useState(false);
  const [localVote, setLocalVote] = useState<'yes' | 'no' | null>(post.userVote ?? null);
  const [localYes, setLocalYes] = useState(post.submission?.yesVotes ?? 0);
  const [localNo, setLocalNo] = useState(post.submission?.noVotes ?? 0);

  useEffect(() => {
    setLocalVote(post.userVote ?? null);
    setLocalYes(post.submission?.yesVotes ?? 0);
    setLocalNo(post.submission?.noVotes ?? 0);
  }, [post.userVote, post.submission?.yesVotes, post.submission?.noVotes]);

  const handleVote = useCallback(async (vote: 'yes' | 'no') => {
    if (voting || !post.submission?.id || localVote) return;
    setVoting(true);
    setLocalVote(vote);
    if (vote === 'yes') setLocalYes((n) => n + 1);
    else setLocalNo((n) => n + 1);
    try {
      const res = await apiFetch<{ yesVotes: number; noVotes: number; movedToReview: boolean }>(
        `/api/domain-submissions/${post.submission.id}/vote`,
        { method: 'POST', body: { vote } },
      );
      setLocalYes(res.yesVotes);
      setLocalNo(res.noVotes);
      queryClient.invalidateQueries({ queryKey: ['community-posts'] });
    } catch {
      setLocalVote(null);
      if (vote === 'yes') setLocalYes((n) => Math.max(0, n - 1));
      else setLocalNo((n) => Math.max(0, n - 1));
    } finally {
      setVoting(false);
    }
  }, [voting, localVote, post.submission?.id, queryClient]);

  const authorDescription = (() => {
    if (post.isBot) return post.botType === 'rebreak' ? t('community.bot_admin') : t('community.bot_ai');
    if (post.isAnonymous || !displayAuthor.id) return undefined;
    const plan = displayAuthor.plan;
    if (plan === 'legend') return t('community.tier_legend');
    if (plan === 'pro') return t('community.tier_pro');
    return t('community.tier_starter');
  })();

  const handleLike = useCallback(async () => {
    if (isLiking) return;
    triggerHeartPop();
    const { newLike, newCount } = applyOptimisticLike(post.id, localLike, localCount);
    setLocalLike(newLike);
    setLocalCount(newCount);
    setIsLiking(true);
    try {
      const res = await apiFetch<{
        likesCount: number;
        dislikesCount: number;
        userLike: 'like' | 'dislike' | null;
      }>('/api/community/like', {
        method: 'POST',
        body: { postId: post.id, type: 'like' },
      });
      setLocalCount(res.likesCount);
      setLocalLike(res.userLike === 'like' ? 'like' : null);
      clearOptimisticLike(post.id);
      // KEIN queryClient.invalidateQueries — würde die komplette Liste neu laden,
      // PostCard remounted, Heart-Pop-Animation abgebrochen. Local-State reicht.
    } catch {
      revertOptimisticLike(post.id);
      setLocalLike(post.userLike === 'like' ? 'like' : null);
      setLocalCount(post.likesCount);
    } finally {
      setIsLiking(false);
    }
  }, [isLiking, localLike, localCount, post.id, post.userLike, post.likesCount, applyOptimisticLike, clearOptimisticLike, revertOptimisticLike, queryClient, triggerHeartPop]);

  return (
    <View style={{ backgroundColor: colors.bg, borderWidth: 1, borderColor: colors.border, borderRadius: 16, padding: 12, marginBottom: 12 }}>
      {/* Repost header */}
      {post.repostOf && (
        <View className="flex-row items-center gap-1.5 mb-3">
          <Ionicons name="repeat" size={14} color="#737373" />
          <Text className="text-xs text-neutral-500" style={{ fontFamily: 'Nunito_400Regular' }}>
            {post.author.nickname} {t('community.reposted_suffix')}
          </Text>
        </View>
      )}

      {/* Author + Meta */}
      <View className="flex-row items-start justify-between mb-2">
        <TouchableOpacity
          activeOpacity={!isLyraPost && !post.isAnonymous && !!displayAuthor.id ? 0.7 : 1}
          onPress={!isLyraPost && !post.isAnonymous && !!displayAuthor.id
            ? () => router.push(`/profile/${displayAuthor.id}`)
            : undefined}
          style={{ flexDirection: 'row', alignItems: 'center', gap: 10, flex: 1 }}
        >
          <View style={{ position: 'relative' }}>
            {isLyraPost ? (
              <RiveAvatar emotion="idle" size="sm" />
            ) : (
              <UserAvatar
                userId={avatarUserId}
                avatar={avatarId}
                nickname={authorLabel}
                size="md"
                isBot={post.isBot}
              />
            )}
          </View>
          <View className="flex-1 min-w-0">
            <Text style={{ fontSize: 14, color: colors.text, fontFamily: 'Nunito_600SemiBold' }} numberOfLines={1}>
              {authorLabel}
            </Text>
            {authorDescription !== undefined && (
              <Text style={{ fontSize: 12, color: colors.textMuted, fontFamily: 'Nunito_400Regular' }}>{authorDescription}</Text>
            )}
          </View>
        </TouchableOpacity>
        <Text style={{ fontSize: 12, color: colors.textMuted, fontFamily: 'Nunito_400Regular', flexShrink: 0, marginLeft: 8, marginTop: 2 }}>
          {formatRelativeTime(post.createdAt)}
        </Text>
      </View>

      {/* Content — hidden for domain_vote (replaced by poll below) */}
      {!!displayContent && post.category !== 'domain_vote' && (
        <Text style={{ fontSize: 14, color: colors.textMuted, fontFamily: 'Nunito_400Regular', lineHeight: 21 }}>
          {displayContent}
        </Text>
      )}

      {/* domain_approved: favicon + domain name + shield badge */}
      {post.category === 'domain_approved' && !!approvedDomain && (
        <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10, marginTop: 12, borderRadius: 12, borderWidth: 1, borderColor: colors.border, backgroundColor: colors.surface, paddingHorizontal: 12, paddingVertical: 8 }}>
          <DomainFavicon domain={approvedDomain} size={24} />
          <View style={{ flex: 1, minWidth: 0 }}>
            <Text style={{ fontSize: 12, color: colors.text, fontFamily: 'Nunito_700Bold' }} numberOfLines={1}>
              {approvedDomain}
            </Text>
            <Text style={{ fontSize: 12, color: colors.textMuted, fontFamily: 'Nunito_400Regular' }}>
              {t('community.domain_added_to_blocklist')}
            </Text>
          </View>
          <HeroShieldCheck size={18} color="#22c55e" />
        </View>
      )}

      {/* domain_vote: poll card with domain banner + yes/no bars + vote buttons */}
      {post.category === 'domain_vote' && !!post.submission && (
        <DomainVoteCard
          submission={post.submission}
          localYes={localYes}
          localNo={localNo}
          localVote={localVote}
          voting={voting}
          isOwnPost={post.author.id === null}
          onVote={handleVote}
          t={t}
        />
      )}

      {/* Image — respektiert echtes Aspect-Ratio (portrait/square/landscape),
           clamped 0.6..1.78 damit 9:21-Storys nicht den ganzen Screen einnehmen. */}
      {!!displayImage && post.category !== 'domain_approved' && post.category !== 'domain_vote' && (
        <Image
          source={{ uri: displayImage }}
          onLoad={(e) => {
            const { width, height } = e.source;
            if (width && height) {
              const ratio = width / height;
              setImageAspectRatio(Math.max(0.6, Math.min(1.78, ratio)));
            }
          }}
          className="w-full rounded-xl mt-3"
          style={{ aspectRatio: imageAspectRatio ?? 1.78 }}
          contentFit="cover"
        />
      )}

      {/* Actions: Like, Comment — not shown for domain_vote */}
      {/* HitSlop +12pt rundum → effektiver Touch-Bereich ~44pt (HIG-Min). */}
      {/* Vorher: Tap-Area = nur Icon-Größe (~21pt) → User-Feedback "reagiert nicht beim 1. Klick". */}
      {post.category !== 'domain_vote' && (
        <View className="flex-row items-center gap-6 mt-3 py-1">
          <Pressable
            onPress={handleLike}
            disabled={isLiking}
            hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
            android_ripple={{ color: 'rgba(220,38,38,0.12)', borderless: true, radius: 22 }}
            className="flex-row items-center gap-1.5"
          >
            <Animated.View style={{ transform: [{ scale: heartScale }] }}>
              <Ionicons
                name={localLike === 'like' ? 'heart' : 'heart-outline'}
                size={20}
                color={localLike === 'like' ? '#dc2626' : '#737373'}
              />
            </Animated.View>
            {localCount > 0 && (
              <Text className="text-xs text-neutral-600" style={{ fontFamily: 'Nunito_600SemiBold' }}>{localCount}</Text>
            )}
          </Pressable>

          <Pressable
            onPress={() => onCommentPress(post.id)}
            hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
            android_ripple={{ color: 'rgba(0,0,0,0.08)', borderless: true, radius: 22 }}
            className="flex-row items-center gap-1.5"
          >
            <Ionicons name="chatbubble-outline" size={19} color="#737373" />
            {post.commentsCount > 0 && (
              <Text className="text-xs text-neutral-600" style={{ fontFamily: 'Nunito_600SemiBold' }}>{post.commentsCount}</Text>
            )}
          </Pressable>
        </View>
      )}
    </View>
  );
}

// React.memo with a shallow compare on the fields that actually drive visible
// content. Without this, every realtime patch (likes/comments/domain-vote)
// re-mapping the posts array would re-render every visible PostCard, even
// though only one item changed. onCommentPress is expected stable from parent.
export const PostCard = memo(PostCardImpl, (prev, next) => {
  if (prev.onCommentPress !== next.onCommentPress) return false;
  const a = prev.post;
  const b = next.post;
  if (a === b) return true;
  if (a.id !== b.id) return false;
  if (a.likesCount !== b.likesCount) return false;
  if (a.dislikesCount !== b.dislikesCount) return false;
  if (a.commentsCount !== b.commentsCount) return false;
  if (a.repostsCount !== b.repostsCount) return false;
  if (a.userLike !== b.userLike) return false;
  if (a.userVote !== b.userVote) return false;
  if (a.content !== b.content) return false;
  if (a.imageUrl !== b.imageUrl) return false;
  if (a.category !== b.category) return false;
  if (a.isAnonymous !== b.isAnonymous) return false;
  if (a.challengeStatus !== b.challengeStatus) return false;
  if (a.isLive !== b.isLive) return false;
  // submission shallow compare on the fields the card reads
  const sa = a.submission;
  const sb = b.submission;
  if (!!sa !== !!sb) return false;
  if (sa && sb) {
    if (sa.id !== sb.id) return false;
    if (sa.status !== sb.status) return false;
    if (sa.yesVotes !== sb.yesVotes) return false;
    if (sa.noVotes !== sb.noVotes) return false;
    if (sa.reviewedAt !== sb.reviewedAt) return false;
  }
  // author identity (anonymous toggling, avatar swap)
  if (a.author.id !== b.author.id) return false;
  if (a.author.avatar !== b.author.avatar) return false;
  if (a.author.nickname !== b.author.nickname) return false;
  if (a.author.plan !== b.author.plan) return false;
  // repostOf identity
  const ra = a.repostOf;
  const rb = b.repostOf;
  if (!!ra !== !!rb) return false;
  if (ra && rb) {
    if (ra.content !== rb.content) return false;
    if (ra.imageUrl !== rb.imageUrl) return false;
    if (ra.author.id !== rb.author.id) return false;
    if (ra.author.nickname !== rb.author.nickname) return false;
    if (ra.author.avatar !== rb.author.avatar) return false;
  }
  return true;
});

// ── Domain Favicon ─────────────────────────────────────────────────────────
// Google S2 favicon-API as in Nuxt PostCard — on error: letter-avatar fallback.
type DomainFaviconProps = { domain: string; size: number };

function DomainFavicon({ domain, size }: DomainFaviconProps) {
  const [failed, setFailed] = useState(false);
  const uri = `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=64`;
  const letter = domain.charAt(0).toUpperCase();

  if (failed) {
    return (
      <View
        style={{ width: size, height: size, borderRadius: 6, backgroundColor: '#e5e7eb' }}
        className="items-center justify-center"
      >
        <Text style={{ fontSize: size * 0.5, color: '#6b7280', fontFamily: 'Nunito_700Bold' }}>
          {letter}
        </Text>
      </View>
    );
  }

  return (
    <Image
      source={{ uri }}
      style={{ width: size, height: size, borderRadius: 6 }}
      contentFit="cover"
      onError={() => setFailed(true)}
    />
  );
}

// ── Domain Vote Poll Card ───────────────────────────────────────────────────
type Submission = NonNullable<CommunityPost['submission']>;

type DomainVoteCardProps = {
  submission: Submission;
  localYes: number;
  localNo: number;
  localVote: 'yes' | 'no' | null;
  voting: boolean;
  isOwnPost: boolean;
  onVote: (v: 'yes' | 'no') => void;
  // TFunction from react-i18next has a complex overload signature; using
  // a simple callable type avoids generics noise here.
  t: (key: string) => string;
};

function DomainVoteCard({
  submission,
  localYes,
  localNo,
  localVote,
  voting,
  isOwnPost,
  onVote,
  t,
}: DomainVoteCardProps) {
  const total = localYes + localNo;
  const yesWidth = total === 0 ? 0 : Math.round((localYes / 10) * 100);
  // No-bar is relative to yes + no total to mirror Nuxt logic
  const noWidth = total === 0 ? 0 : Math.round((localNo / Math.max(total, localYes)) * 100);

  const isPending = submission.status === 'pending' || submission.status === 'in_review';
  const isApproved = submission.status === 'approved';

  const statusLabel = (() => {
    if (isApproved) return submission.reviewedAt ? `Approved ${formatApprovedDate(submission.reviewedAt)}` : 'Global';
    if (submission.status === 'rejected') return t('community.vote_rejected');
    if (submission.status === 'in_review') return t('community.vote_in_review');
    return `${localYes} / 10`;
  })();

  return (
    <View className="mt-1 gap-2.5">
      {/* Header: label + status badge */}
      <View className="flex-row items-center justify-between gap-2">
        <View className="flex-row items-center gap-1.5">
          <Ionicons name="shield-checkmark-outline" size={14} color="#737373" />
          <Text className="text-xs text-neutral-500" style={{ fontFamily: 'Nunito_400Regular' }}>
            {t('community.domain_proposal_label')}
          </Text>
        </View>
        <View
          className={`px-2 py-0.5 rounded-full ${isApproved ? 'bg-green-100' : 'bg-neutral-100'}`}
        >
          <Text
            className={`text-[10px] ${isApproved ? 'text-green-700' : 'text-neutral-500'}`}
            style={{ fontFamily: 'Nunito_600SemiBold' }}
          >
            {statusLabel}
          </Text>
        </View>
      </View>

      {/* Domain card */}
      <View className="flex-row items-center gap-3 rounded-xl px-3 py-2.5 border border-neutral-200 bg-neutral-50">
        <DomainFavicon domain={submission.domain} size={28} />
        <View className="flex-1 min-w-0">
          <Text className="text-sm font-semibold text-neutral-900 truncate" style={{ fontFamily: 'Nunito_700Bold' }}>
            {submission.domain}
          </Text>
          <Text className="text-[10px] text-neutral-400" style={{ fontFamily: 'Nunito_400Regular' }}>
            {isApproved ? t('community.domain_added') : t('community.domain_proposed')}
          </Text>
        </View>
        <Ionicons
          name={isApproved ? 'shield-checkmark' : 'shield-half-outline'}
          size={20}
          color="#a3a3a3"
        />
      </View>

      {/* Yes bar */}
      <View>
        <View className="flex-row items-center justify-between mb-1">
          <View className="flex-row items-center gap-1">
            <Ionicons name="thumbs-up-outline" size={12} color="#525252" />
            <Text className="text-[11px] text-neutral-700" style={{ fontFamily: 'Nunito_600SemiBold' }}>
              {t('community.vote_yes')}
            </Text>
          </View>
          <Text className="text-[11px] text-neutral-400" style={{ fontFamily: 'Nunito_400Regular' }}>
            {localYes} / 10
          </Text>
        </View>
        <View className="h-1.5 bg-neutral-100 rounded-full overflow-hidden">
          <View className="h-full bg-rebreak-500 rounded-full" style={{ width: `${yesWidth}%` }} />
        </View>
      </View>

      {/* No bar */}
      <View>
        <View className="flex-row items-center justify-between mb-1">
          <View className="flex-row items-center gap-1">
            <Ionicons name="thumbs-down-outline" size={12} color="#525252" />
            <Text className="text-[11px] text-neutral-700" style={{ fontFamily: 'Nunito_600SemiBold' }}>
              {t('community.vote_no')}
            </Text>
          </View>
          <Text className="text-[11px] text-neutral-400" style={{ fontFamily: 'Nunito_400Regular' }}>
            {localNo}
          </Text>
        </View>
        <View className="h-1.5 bg-neutral-100 rounded-full overflow-hidden">
          <View className="h-full bg-red-400 rounded-full" style={{ width: `${noWidth}%` }} />
        </View>
      </View>

      {/* Vote buttons — only for pending + not own post + not already voted */}
      {isPending && !isOwnPost && !localVote && (
        <View className="flex-row gap-2 pt-0.5">
          <Pressable
            onPress={() => onVote('yes')}
            disabled={voting}
            className="flex-1 flex-row items-center justify-center gap-1.5 h-9 rounded-xl border border-rebreak-500"
            style={{ opacity: voting ? 0.5 : 1 }}
          >
            <Ionicons name="thumbs-up" size={14} color="#f97316" />
            <Text className="text-sm text-rebreak-500" style={{ fontFamily: 'Nunito_600SemiBold' }}>
              {t('community.vote_yes')}
            </Text>
          </Pressable>
          <Pressable
            onPress={() => onVote('no')}
            disabled={voting}
            className="flex-1 flex-row items-center justify-center gap-1.5 h-9 rounded-xl border border-neutral-300"
            style={{ opacity: voting ? 0.5 : 1 }}
          >
            <Ionicons name="thumbs-down" size={14} color="#737373" />
            <Text className="text-sm text-neutral-500" style={{ fontFamily: 'Nunito_600SemiBold' }}>
              {t('community.vote_no')}
            </Text>
          </Pressable>
        </View>
      )}

      {/* Already voted indicator */}
      {isPending && !isOwnPost && !!localVote && (
        <Text className="text-[11px] text-center text-neutral-400 pt-0.5" style={{ fontFamily: 'Nunito_400Regular' }}>
          {t('community.voted_thanks')}
        </Text>
      )}

      {/* Own post indicator */}
      {isPending && isOwnPost && (
        <Text className="text-[11px] text-center text-neutral-400 pt-0.5" style={{ fontFamily: 'Nunito_400Regular' }}>
          {t('community.domain_vote_own')}
        </Text>
      )}
    </View>
  );
}

function formatApprovedDate(dateStr: string): string {
  try {
    const d = new Date(dateStr);
    return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
  } catch { return ''; }
}
