/**
 * PostCard — Twitter-style feed row.
 *
 * Layout (top-to-bottom, left-to-right):
 *
 *   [avatar 44]    [@author · time · ⋯ menu]
 *                  [post text body with #tags + @mentions highlighted]
 *                  [optional attachment preview]
 *                  [💬 0   🔁 link   ❤️ likes   👁 views]
 *
 * Interaction model:
 *   - Tap anywhere except controls → navigate to post detail
 *   - Tap author/avatar → profile
 *   - Double-tap the post body → like (with a short heart-bounce animation)
 *   - Long-press → context menu (copy, share link, delete-if-mine)
 *
 * Performance notes:
 *   - Memoised. Feed lists re-render often (after every like, view bump,
 *     new post), but each card only needs to update when ITS own stats
 *     change. We use shallow prop comparison + stable key on post_id.
 *   - Stats are passed in by parent (fetched once per refresh), not
 *     fetched here — avoids N /stats requests per timeline render.
 */
import React, { useState, useCallback, useMemo } from 'react';
import {
  View, Text, Pressable, Alert, Animated, Image,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { router } from 'expo-router';

import { Avatar } from '@/components/Avatar';
import { useStore } from '@/lib/store';
import { getNodeUrl } from '@/lib/api';
import type { FeedPostItem } from '@/lib/feed';
import {
  formatRelativeTime, formatCount, likePost, unlikePost, deletePost,
} from '@/lib/feed';

export interface PostCardProps {
  post: FeedPostItem;
  /** true = current user has liked this post (used for filled heart). */
  likedByMe?: boolean;
  /** Called after a successful like/unlike so parent can refresh stats. */
  onStatsChanged?: (postID: string) => void;
  /** Called after delete so parent can drop the card from the list. */
  onDeleted?: (postID: string) => void;
  /** Compact (no attachment, less padding) — used in nested thread context. */
  compact?: boolean;
}

function PostCardInner({ post, likedByMe, onStatsChanged, onDeleted, compact }: PostCardProps) {
  const keyFile = useStore(s => s.keyFile);
  const contacts = useStore(s => s.contacts);

  // Optimistic local state — immediate response to tap, reconciled after tx.
  const [localLiked, setLocalLiked] = useState<boolean>(!!likedByMe);
  const [localLikeCount, setLocalLikeCount] = useState<number>(post.likes);
  const [busy, setBusy] = useState(false);

  React.useEffect(() => {
    setLocalLiked(!!likedByMe);
    setLocalLikeCount(post.likes);
  }, [likedByMe, post.likes]);

  // Heart bounce animation when liked (Twitter-style).
  const heartScale = useMemo(() => new Animated.Value(1), []);
  const animateHeart = useCallback(() => {
    heartScale.setValue(0.6);
    Animated.spring(heartScale, {
      toValue: 1,
      friction: 3,
      tension: 120,
      useNativeDriver: true,
    }).start();
  }, [heartScale]);

  const mine = !!keyFile && keyFile.pub_key === post.author;

  // Find a display-friendly name for the author. If it's a known contact
  // with @username, use that; otherwise short-addr.
  const displayName = useMemo(() => {
    const c = contacts.find(x => x.address === post.author);
    if (c?.username) return `@${c.username}`;
    if (c?.alias) return c.alias;
    if (mine) return 'You';
    return shortAddr(post.author);
  }, [contacts, post.author, mine]);

  const onToggleLike = useCallback(async () => {
    if (!keyFile || busy) return;
    setBusy(true);
    const wasLiked = localLiked;
    // Optimistic update.
    setLocalLiked(!wasLiked);
    setLocalLikeCount(c => c + (wasLiked ? -1 : 1));
    animateHeart();
    try {
      if (wasLiked) {
        await unlikePost({ from: keyFile.pub_key, privKey: keyFile.priv_key, postID: post.post_id });
      } else {
        await likePost({ from: keyFile.pub_key, privKey: keyFile.priv_key, postID: post.post_id });
      }
      // Refresh stats from server so counts reconcile (on-chain is delayed
      // by block time; server returns current cached count).
      setTimeout(() => onStatsChanged?.(post.post_id), 1500);
    } catch (e: any) {
      // Roll back optimistic update.
      setLocalLiked(wasLiked);
      setLocalLikeCount(c => c + (wasLiked ? 1 : -1));
      Alert.alert('Не удалось', String(e?.message ?? e));
    } finally {
      setBusy(false);
    }
  }, [keyFile, busy, localLiked, post.post_id, animateHeart, onStatsChanged]);

  const onOpenDetail = useCallback(() => {
    router.push(`/(app)/feed/${post.post_id}` as never);
  }, [post.post_id]);

  const onOpenAuthor = useCallback(() => {
    router.push(`/(app)/profile/${post.author}` as never);
  }, [post.author]);

  const onLongPress = useCallback(() => {
    if (!keyFile) return;
    const options: Array<{ label: string; destructive?: boolean; onPress: () => void }> = [];
    if (mine) {
      options.push({
        label: 'Удалить пост',
        destructive: true,
        onPress: () => {
          Alert.alert('Удалить пост?', 'Это действие нельзя отменить.', [
            { text: 'Отмена', style: 'cancel' },
            {
              text: 'Удалить',
              style: 'destructive',
              onPress: async () => {
                try {
                  await deletePost({
                    from: keyFile.pub_key,
                    privKey: keyFile.priv_key,
                    postID: post.post_id,
                  });
                  onDeleted?.(post.post_id);
                } catch (e: any) {
                  Alert.alert('Ошибка', String(e?.message ?? e));
                }
              },
            },
          ]);
        },
      });
    }
    if (options.length === 0) return;
    const buttons: Array<{ text: string; style?: 'default' | 'cancel' | 'destructive'; onPress?: () => void }> = [
      ...options.map(o => ({
        text: o.label,
        style: (o.destructive ? 'destructive' : 'default') as 'default' | 'destructive',
        onPress: o.onPress,
      })),
      { text: 'Отмена', style: 'cancel' as const },
    ];
    Alert.alert('Действия', '', buttons);
  }, [keyFile, mine, post.post_id, onDeleted]);

  // Attachment preview URL — native Image can stream straight from the
  // hosting relay's /feed/post/{id}/attachment endpoint. `getNodeUrl()`
  // returns the node the client is connected to; for cross-relay posts
  // that's actually the hosting relay once /api/relays resolution lands
  // (Phase D). For now we assume same-node.
  const attachmentURL = post.has_attachment
    ? `${getNodeUrl()}/feed/post/${post.post_id}/attachment`
    : null;

  // Body content truncation:
  //  - In the timeline (compact=undefined/false) cap at 5 lines. If the
  //    text is longer the rest is hidden behind "…" — tapping the card
  //    opens the detail view where the full body is shown.
  //  - In post detail (compact=true) show everything.
  const bodyLines = compact ? undefined : 5;

  return (
    <Pressable
      onPress={onOpenDetail}
      onLongPress={onLongPress}
      style={({ pressed }) => ({
        flexDirection: 'row',
        paddingHorizontal: 16,
        paddingVertical: compact ? 10 : 12,
        backgroundColor: pressed ? '#080808' : 'transparent',
      })}
    >
      {/* Avatar column */}
      <Pressable onPress={onOpenAuthor} hitSlop={4}>
        <Avatar name={displayName} address={post.author} size={44} />
      </Pressable>

      {/* Content column */}
      <View style={{ flex: 1, marginLeft: 10, minWidth: 0 }}>
        {/* Header: [name] · [time]  …  [menu]
            All three on a single row with no wrap. The name shrinks if
            too long (flexShrink:1 + numberOfLines:1), time never shrinks
            — so long handles get truncated with ellipsis while "2h"
            stays readable.

            Whole header is inside a single Pressable so a tap anywhere
            on the header opens the author's profile — matches Twitter's
            behaviour where the name-row is a big hit target. */}
        <Pressable
          onPress={onOpenAuthor}
          hitSlop={{ top: 4, bottom: 2 }}
          style={{ flexDirection: 'row', alignItems: 'center' }}
        >
          <Text
            numberOfLines={1}
            style={{
              color: '#ffffff',
              fontWeight: '700',
              fontSize: 14,
              letterSpacing: -0.2,
              flexShrink: 1,
            }}
          >
            {displayName}
          </Text>
          <Text
            style={{ color: '#6a6a6a', fontSize: 13, marginHorizontal: 4 }}
            numberOfLines={1}
          >
            ·
          </Text>
          <Text
            style={{ color: '#6a6a6a', fontSize: 13 }}
            numberOfLines={1}
          >
            {formatRelativeTime(post.created_at)}
          </Text>
          <View style={{ flex: 1 }} />
          {mine && (
            <Pressable onPress={onLongPress} hitSlop={8}>
              <Ionicons name="ellipsis-horizontal" size={16} color="#6a6a6a" />
            </Pressable>
          )}
        </Pressable>

        {/* Body text with hashtag highlighting */}
        {post.content.length > 0 && (
          <Text
            numberOfLines={bodyLines}
            ellipsizeMode="tail"
            style={{
              color: '#ffffff',
              fontSize: 15,
              lineHeight: 20,
              marginTop: 2,
            }}
          >
            {renderInline(post.content)}
          </Text>
        )}

        {/* Attachment preview.
            Timeline (compact=false): aspect-ratio capped at 4:5 so a
            portrait photo doesn't occupy the whole screen — extra height
            is cropped via resizeMode="cover", full image shows in detail.
            Detail (compact=true): contain with no aspect cap → original
            proportions preserved. */}
        {attachmentURL && (
          <View
            style={{
              marginTop: 10,
              borderRadius: 14,
              overflow: 'hidden',
              backgroundColor: '#0a0a0a',
              borderWidth: 1,
              borderColor: '#1f1f1f',
            }}
          >
            <Image
              source={{ uri: attachmentURL }}
              style={compact ? {
                width:   '100%',
                aspectRatio: 1,  // will be overridden by onLoad if known
                maxHeight: undefined,
              } : {
                width:   '100%',
                aspectRatio: 4 / 5,   // portrait-friendly but bounded
              }}
              resizeMode={compact ? 'contain' : 'cover'}
            />
          </View>
        )}

        {/* Action row — 3 buttons (Twitter-style). Comments button
            intentionally omitted: v2.0.0 doesn't implement replies and
            a dead button with "0" adds noise. Heart + views + share
            distribute across the row; share pins to the right edge. */}
        <View
          style={{
            flexDirection: 'row',
            alignItems: 'center',
            justifyContent: 'space-between',
            marginTop: 12,
            paddingHorizontal: 12,
          }}
        >
          <View style={{ flex: 1, alignItems: 'flex-start' }}>
            <Pressable
              onPress={onToggleLike}
              disabled={busy}
              hitSlop={8}
              style={({ pressed }) => ({
                flexDirection: 'row', alignItems: 'center', gap: 6,
                opacity: pressed ? 0.5 : 1,
              })}
            >
              <Animated.View style={{ transform: [{ scale: heartScale }] }}>
                <Ionicons
                  name={localLiked ? 'heart' : 'heart-outline'}
                  size={16}
                  color={localLiked ? '#e0245e' : '#6a6a6a'}
                />
              </Animated.View>
              <Text
                style={{
                  color: localLiked ? '#e0245e' : '#6a6a6a',
                  fontSize: 12,
                  fontWeight: localLiked ? '600' : '400',
                }}
              >
                {formatCount(localLikeCount)}
              </Text>
            </Pressable>
          </View>
          <View style={{ flex: 1, alignItems: 'flex-start' }}>
            <ActionButton
              icon="eye-outline"
              label={formatCount(post.views)}
            />
          </View>
          <View style={{ alignItems: 'flex-end' }}>
            <ActionButton
              icon="share-outline"
              onPress={() => {
                // Placeholder — copy postID to clipboard in a future PR.
                Alert.alert('Ссылка', `dchain://post/${post.post_id}`);
              }}
            />
          </View>
        </View>
      </View>
    </Pressable>
  );
}

export const PostCard = React.memo(PostCardInner);

/**
 * PostSeparator — visible divider line between post cards. Exported so
 * every feed surface (timeline, author, hashtag, post detail) can pass
 * it as ItemSeparatorComponent and get identical spacing / colour.
 *
 * Layout: 12px blank space → 1px grey line → 12px blank space. The
 * blank space on each side makes the line "float" between posts rather
 * than hugging the edge of the card — gives clear visual separation
 * without needing big card padding everywhere.
 *
 * Colour #2a2a2a is the minimum grey that reads on OLED black under
 * mobile-bright-mode gamma; darker and the seam vanishes.
 */
export function PostSeparator() {
  return (
    <View style={{ paddingVertical: 12 }}>
      <View style={{ height: 1, backgroundColor: '#2a2a2a' }} />
    </View>
  );
}

// ── Inline helpers ──────────────────────────────────────────────────────

/** ActionButton — small icon + optional label. */
function ActionButton({ icon, label, onPress }: {
  icon: React.ComponentProps<typeof Ionicons>['name'];
  label?: string;
  onPress?: () => void;
}) {
  return (
    <Pressable
      onPress={onPress}
      hitSlop={8}
      disabled={!onPress}
      style={({ pressed }) => ({
        flexDirection: 'row', alignItems: 'center', gap: 6,
        opacity: pressed ? 0.5 : 1,
      })}
    >
      <Ionicons name={icon} size={16} color="#6a6a6a" />
      {label && (
        <Text style={{ color: '#6a6a6a', fontSize: 12 }}>{label}</Text>
      )}
    </Pressable>
  );
}

/**
 * Render post body with hashtag highlighting. Splits by the hashtag regex,
 * wraps matches in blue-coloured Text spans that are tappable → hashtag
 * feed. For future: @mentions highlighting + URL auto-linking.
 */
function renderInline(text: string): React.ReactNode {
  const parts = text.split(/(#[A-Za-z0-9_\u0400-\u04FF]{1,40})/g);
  return parts.map((part, i) => {
    if (part.startsWith('#')) {
      const tag = part.slice(1);
      return (
        <Text
          key={i}
          style={{ color: '#1d9bf0' }}
          onPress={() => router.push(`/(app)/feed/tag/${encodeURIComponent(tag)}` as never)}
        >
          {part}
        </Text>
      );
    }
    return (
      <Text key={i}>{part}</Text>
    );
  });
}

function shortAddr(a: string, n = 6): string {
  if (!a) return '—';
  return a.length <= n * 2 + 1 ? a : `${a.slice(0, n)}…${a.slice(-n)}`;
}

