/**
 * PostCard 主组件
 * 帖子卡片组件（新实现）
 *
 * @example
 * // 新 API 使用方式
 * <PostCard
 *   post={post}
 *   onAction={(action) => handleAction(post, action)}
 *   variant="list"
 *   features="full"
 * />
 */

import React, { useMemo, useState, memo, useEffect } from 'react';
import { View, TouchableOpacity, StyleSheet, Alert, TextStyle } from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { PostCardProps, PostCardAction } from './types';
import { ImageGridItem, SmartImage } from '../../common';
import Text from '../../common/Text';
import Avatar from '../../common/Avatar';
import { spacing, borderRadius, fontSizes, useAppColors, type AppColors } from '../../../theme';
import { getPreviewImageUrl } from '../../../utils/imageHelper';
import PostImages from './components/PostImages';
import { useResponsive } from '../../../hooks/useResponsive';
import { formatPostCreatedAtString } from '../../../core/entities/Post';

/** 列表相对时间：独立定时刷新，避免外层 PostCard.memo 挡住「分钟前」更新 */
const PostCardRelativeTime: React.FC<{ createdAt?: string | null; style?: TextStyle | TextStyle[] }> = ({
  createdAt,
  style: timeStyle,
}) => {
  const [label, setLabel] = useState(() => formatPostCreatedAtString(createdAt));

  useEffect(() => {
    setLabel(formatPostCreatedAtString(createdAt));
    const tick = () => setLabel(formatPostCreatedAtString(createdAt));
    const id = setInterval(tick, 60_000);
    return () => clearInterval(id);
  }, [createdAt]);

  return (
    <Text style={timeStyle} numberOfLines={1}>
      {label}
    </Text>
  );
};

function imagesSignature(
  images: PostCardProps['post']['images'] | undefined
): string {
  if (!images?.length) return '';
  return images.map((i) => i.id).join('\u001f');
}

function authorSignature(author: PostCardProps['post']['author']): string {
  if (!author) return '';
  return [author.id, author.nickname ?? '', author.avatar ?? ''].join('\u001f');
}

function topCommentSignature(tc: PostCardProps['post']['top_comment']): string {
  if (!tc) return '';
  return [tc.id, tc.content ?? '', tc.author?.id ?? ''].join('\u001f');
}

function channelSignature(ch: PostCardProps['post']['channel']): string {
  if (!ch) return '';
  return [ch.id, ch.name ?? ''].join('\u001f');
}

function featuresComparable(f: PostCardProps['features']): string {
  if (f == null) return '';
  if (typeof f === 'string') return f;
  return JSON.stringify(f);
}

/**
 * 自定义比较：忽略 onAction 引用（父组件应用 postId + ref 解析最新 post，避免 Tab 切换时全列表因回调重建而重绘）。
 * index 未参与 UI，不比较以免列表重排时多余更新。
 */
function arePostCardPropsEqual(prev: PostCardProps, next: PostCardProps): boolean {
  if (prev.variant !== next.variant) return false;
  if (prev.isPostAuthor !== next.isPostAuthor) return false;
  if (prev.isAuthor !== next.isAuthor) return false;
  if (prev.style !== next.style) return false;
  if (featuresComparable(prev.features) !== featuresComparable(next.features)) return false;

  const a = prev.post;
  const b = next.post;
  if (a === b) return true;

  if (a.id !== b.id) return false;
  if (a.title !== b.title) return false;
  if (a.content !== b.content) return false;
  if ((a.likes_count ?? 0) !== (b.likes_count ?? 0)) return false;
  if ((a.comments_count ?? 0) !== (b.comments_count ?? 0)) return false;
  if ((a.favorites_count ?? 0) !== (b.favorites_count ?? 0)) return false;
  if ((a.shares_count ?? 0) !== (b.shares_count ?? 0)) return false;
  if ((a.views_count ?? 0) !== (b.views_count ?? 0)) return false;
  if (!!a.is_pinned !== !!b.is_pinned) return false;
  if (!!a.is_locked !== !!b.is_locked) return false;
  if (!!a.is_vote !== !!b.is_vote) return false;
  if (a.created_at !== b.created_at) return false;
  if (a.updated_at !== b.updated_at) return false;
  if (a.content_edited_at !== b.content_edited_at) return false;
  if (!!a.is_liked !== !!b.is_liked) return false;
  if (!!a.is_favorited !== !!b.is_favorited) return false;
  if (authorSignature(a.author) !== authorSignature(b.author)) return false;
  if (imagesSignature(a.images) !== imagesSignature(b.images)) return false;
  if (topCommentSignature(a.top_comment) !== topCommentSignature(b.top_comment)) return false;
  if (channelSignature(a.channel) !== channelSignature(b.channel)) return false;

  return true;
}

function createPostCardStyles(colors: AppColors) {
  return StyleSheet.create({
    card: {
      backgroundColor: colors.background.paper,
      borderRadius: borderRadius.lg,
      padding: spacing.md,
      borderWidth: StyleSheet.hairlineWidth,
      borderColor: colors.divider,
    },
    header: {
      flexDirection: 'row',
      alignItems: 'center',
      justifyContent: 'space-between',
      marginBottom: spacing.sm,
    },
    authorWrap: {
      flexDirection: 'row',
      alignItems: 'center',
      flex: 1,
      minWidth: 0,
    },
    authorMeta: {
      marginLeft: spacing.sm,
      flex: 1,
      minWidth: 0,
    },
    authorName: {
      color: colors.text.primary,
      fontWeight: '600',
      fontSize: fontSizes.md,
    },
    timeText: {
      color: colors.text.hint,
      fontSize: fontSizes.xs,
      marginTop: 2,
    },
    metaRow: {
      flexDirection: 'row',
      alignItems: 'center',
      gap: 6,
      marginTop: 2,
      flexWrap: 'wrap',
    },
    channelTag: {
      flexDirection: 'row',
      alignItems: 'center',
      maxWidth: 140,
      paddingHorizontal: 6,
      paddingVertical: 2,
      borderRadius: borderRadius.sm,
      backgroundColor: `${colors.primary.main}12`,
      gap: 4,
    },
    channelTagText: {
      fontSize: fontSizes.xs,
      color: colors.primary.main,
      fontWeight: '600',
      flexShrink: 1,
    },
    pinnedTag: {
      flexDirection: 'row',
      alignItems: 'center',
      backgroundColor: `${colors.warning.light}22`,
      borderRadius: borderRadius.sm,
      paddingHorizontal: 4,
      paddingVertical: 1,
      gap: 2,
    },
    pinnedText: {
      color: colors.warning.main,
      fontSize: fontSizes.xs,
    },
    badgeText: {
      color: colors.primary.main,
      fontSize: fontSizes.xs,
      fontWeight: '600',
    },
    deleteButton: {
      padding: spacing.xs,
      marginLeft: spacing.sm,
    },
    title: {
      color: colors.text.primary,
      fontSize: fontSizes.lg,
      fontWeight: '600',
      marginBottom: spacing.xs,
    },
    content: {
      color: colors.text.secondary,
      fontSize: fontSizes.md,
      marginBottom: spacing.xs,
      lineHeight: fontSizes.md * 1.45,
    },
    expandBtn: {
      marginBottom: spacing.sm,
    },
    expandText: {
      color: colors.primary.main,
      fontSize: fontSizes.sm,
    },
    topCommentBox: {
      backgroundColor: colors.background.default,
      borderRadius: borderRadius.sm,
      paddingHorizontal: spacing.sm,
      paddingVertical: spacing.xs,
      marginBottom: spacing.sm,
      flexDirection: 'row',
      alignItems: 'center',
    },
    topCommentAuthor: {
      color: colors.text.secondary,
      fontSize: fontSizes.sm,
      fontWeight: '600',
      marginRight: 4,
    },
    topCommentText: {
      color: colors.text.secondary,
      fontSize: fontSizes.sm,
      flex: 1,
    },
    gridCover: {
      width: '100%',
      aspectRatio: 0.78,
      backgroundColor: colors.background.default,
    },
    actions: {
      flexDirection: 'row',
      justifyContent: 'space-between',
      alignItems: 'center',
    },
    actionsLeading: {
      flexDirection: 'row',
      alignItems: 'center',
      flex: 1,
      minWidth: 0,
      marginRight: spacing.sm,
      gap: spacing.sm,
    },
    channelTagAfterViews: {
      maxWidth: 130,
      flexShrink: 1,
    },
    viewsWrap: {
      flexDirection: 'row',
      alignItems: 'center',
      flexShrink: 0,
    },
    viewsText: {
      color: colors.text.hint,
      fontSize: fontSizes.sm,
      marginLeft: 4,
    },
    actionButtons: {
      flexDirection: 'row',
      alignItems: 'center',
    },
    actionBtn: {
      flexDirection: 'row',
      alignItems: 'center',
      marginLeft: spacing.md,
    },
    actionText: {
      color: colors.text.secondary,
      fontSize: fontSizes.sm,
      marginLeft: 4,
    },
    activeActionText: {
      color: colors.error.main,
    },
    activeActionTextMerged: {
      color: colors.error.main,
      fontSize: fontSizes.sm,
      marginLeft: 4,
    },
    gridRootCard: {
      backgroundColor: colors.background.paper,
      borderRadius: borderRadius.lg,
      overflow: 'hidden',
      borderWidth: StyleSheet.hairlineWidth,
      borderColor: colors.divider,
    },
    gridNoImagePreview: {
      backgroundColor: colors.background.default,
      margin: 6,
      borderRadius: borderRadius.md,
      minHeight: 120,
      padding: 8,
    },
    gridNoImageText: {
      color: colors.text.secondary,
      fontSize: fontSizes.sm,
      lineHeight: 20,
    },
    gridVoteTag: {
      position: 'absolute',
      top: 8,
      right: 8,
      backgroundColor: `${colors.primary.main}CC`,
      borderRadius: borderRadius.full,
      paddingHorizontal: 8,
      paddingVertical: 4,
      flexDirection: 'row',
      alignItems: 'center',
      gap: 4,
    },
    gridVoteTagText: {
      color: colors.primary.contrast,
      fontSize: 10,
      fontWeight: '600',
    },
    gridTitleMain: {
      color: colors.text.primary,
      fontSize: fontSizes.md,
      lineHeight: 20,
      paddingHorizontal: 8,
      paddingTop: 8,
      minHeight: 44,
    },
    gridChannelRow: {
      flexDirection: 'row',
      alignItems: 'center',
      gap: 4,
      paddingHorizontal: 8,
      paddingTop: 4,
      paddingBottom: 2,
    },
    gridChannelText: {
      fontSize: fontSizes.xs,
      color: colors.text.secondary,
      fontWeight: '600',
      flex: 1,
      minWidth: 0,
    },
    gridFooter: {
      flexDirection: 'row',
      alignItems: 'center',
      justifyContent: 'space-between',
      paddingHorizontal: 8,
      paddingVertical: 10,
    },
    gridUserArea: {
      flexDirection: 'row',
      alignItems: 'center',
      flex: 1,
      minWidth: 0,
    },
    gridUsername: {
      color: colors.text.secondary,
      fontSize: fontSizes.sm,
      marginLeft: 6,
      flex: 1,
    },
    gridLikeArea: {
      flexDirection: 'row',
      alignItems: 'center',
    },
    gridLikeCount: {
      color: colors.text.secondary,
      fontSize: fontSizes.sm,
      marginLeft: 4,
    },
  });
}

/**
 * PostCard 主组件
 * 仅支持新 API
 */
const PostCardInner: React.FC<PostCardProps> = (normalizedProps) => {
  const {
    post,
    onAction,
    variant = 'list',
    features,
    isPostAuthor = false,
    isAuthor = false,
    index,
    style,
  } = normalizedProps;

  const colors = useAppColors();
  const styles = useMemo(() => createPostCardStyles(colors), [colors]);
  const [isExpanded, setIsExpanded] = useState(false);
  const [isDeleting, setIsDeleting] = useState(false);

  const isCompact =
    features === 'compact' ||
    (typeof features === 'object' && features?.showContent === false);

  const emit = (action: PostCardAction) => {
    onAction(action);
  };

  const handleImagePress = (images: ImageGridItem[], imageIndex: number) => {
    emit({ type: 'imagePress', payload: { images, imageIndex } });
  };

  const showGrid = variant === 'grid';
  const author = post.author;
  const rawContent = post.content ?? '';
  const { isDesktop, isTablet, isWideScreen, isLandscape } = useResponsive();

  const handleCardPress = () => emit({ type: 'press' });
  const handleUserPress = () => emit({ type: 'userPress' });
  const handleLike = () => emit({ type: post.is_liked ? 'unlike' : 'like' });
  const handleComment = () => emit({ type: 'comment' });
  const handleBookmark = () => emit({ type: post.is_favorited ? 'unbookmark' : 'bookmark' });
  const handleShare = () => emit({ type: 'share' });
  const handleDelete = () => {
    if (isDeleting) return;
    Alert.alert(
      '删除帖子',
      '确定要删除这篇帖子吗？删除后将无法恢复。',
      [
        { text: '取消', style: 'cancel' },
        {
          text: '删除',
          style: 'destructive',
          onPress: async () => {
            setIsDeleting(true);
            try {
              emit({ type: 'delete' });
            } finally {
              setIsDeleting(false);
            }
          },
        },
      ],
    );
  };

  const images: ImageGridItem[] = Array.isArray(post.images)
    ? post.images.map((img) => ({
        id: img.id,
        url: img.url,
        thumbnail_url: img.thumbnail_url,
        preview_url: img.preview_url,
        preview_url_large: img.preview_url_large,
        width: img.width,
        height: img.height,
      }))
    : [];

  const formatNumber = (num: number): string => {
    if (num >= 10000) return `${(num / 10000).toFixed(1)}w`;
    if (num >= 1000) return `${(num / 1000).toFixed(1)}k`;
    return String(num);
  };

  const getResponsiveMaxLength = () => {
    if (isWideScreen) return 300;
    if (isDesktop) return 250;
    if (isTablet) return 200;
    return 100;
  };

  const contentNumberOfLines = useMemo(() => {
    if (isWideScreen) return 8;
    if (isDesktop) return 6;
    if (isTablet) return 5;
    return 3;
  }, [isWideScreen, isDesktop, isTablet]);

  const getTruncatedContent = (value: string): string => {
    const maxLength = getResponsiveMaxLength();
    if (value.length <= maxLength || isExpanded) return value;
    return `${value.substring(0, maxLength)}...`;
  };

  const shouldTruncate = !showGrid && !isCompact && rawContent.length > getResponsiveMaxLength();
  const content = useMemo(
    () => (showGrid || isCompact ? rawContent : getTruncatedContent(rawContent)),
    [rawContent, showGrid, isCompact, isExpanded, isWideScreen, isDesktop, isTablet]
  );

  const renderImages = () => {
    if (images.length === 0) return null;

    if (showGrid) {
      const cover = images[0];
      const coverUrl = getPreviewImageUrl(cover as any, 'grid');
      return (
        <TouchableOpacity activeOpacity={0.9} onPress={() => handleImagePress(images, 0)}>
          <SmartImage
            source={{ uri: coverUrl || cover.url || '' }}
            style={styles.gridCover}
            resizeMode="cover"
            usePreview={true}
          />
        </TouchableOpacity>
      );
    }

    return (
      <PostImages
        images={post.images || []}
        displayMode="list"
        onImagePress={handleImagePress}
      />
    );
  };

  const renderTopComment = () => {
    if (showGrid || !post.top_comment) return null;
    return (
      <TouchableOpacity style={styles.topCommentBox} activeOpacity={0.85} onPress={handleComment}>
        <Text style={styles.topCommentAuthor} numberOfLines={1}>
          {post.top_comment.author?.nickname || '匿名用户'}:
        </Text>
        <Text style={styles.topCommentText} numberOfLines={1}>
          {post.top_comment.content}
        </Text>
      </TouchableOpacity>
    );
  };

  const renderGridCard = () => {
    const cover = images[0];
    const hasImage = !!cover;
    const coverUrl = hasImage ? getPreviewImageUrl(cover as any, 'grid') : '';
    const contentPreview = rawContent
      .replace(/[#*_~`\[\]()]/g, '')
      .replace(/\n+/g, ' ')
      .trim();

    return (
      <TouchableOpacity
        activeOpacity={0.9}
        onPress={handleCardPress}
        style={[styles.gridRootCard, style]}
      >
        {hasImage ? (
          <TouchableOpacity activeOpacity={0.9} onPress={() => handleImagePress(images, 0)}>
            <SmartImage
              source={{ uri: coverUrl || cover.url || '' }}
              style={styles.gridCover}
              resizeMode="cover"
              usePreview={true}
            />
          </TouchableOpacity>
        ) : (
          <View style={styles.gridNoImagePreview}>
            <Text style={styles.gridNoImageText} numberOfLines={6}>
              {contentPreview}
            </Text>
          </View>
        )}

        {post.is_vote && (
          <View style={styles.gridVoteTag}>
            <MaterialCommunityIcons name="vote" size={11} color={colors.primary.contrast} />
            <Text style={styles.gridVoteTagText}>投票</Text>
          </View>
        )}

        {!!post.title && (
          <Text style={styles.gridTitleMain} numberOfLines={hasImage ? 2 : 3}>
            {post.title}
          </Text>
        )}

        {!!post.channel?.name && (
          <View style={styles.gridChannelRow}>
            <MaterialCommunityIcons name="tag-outline" size={12} color={colors.text.hint} />
            <Text style={styles.gridChannelText} numberOfLines={1}>
              {post.channel.name}
            </Text>
          </View>
        )}

        <View style={styles.gridFooter}>
          <TouchableOpacity onPress={handleUserPress} style={styles.gridUserArea}>
            <Avatar source={author?.avatar} size={20} name={author?.nickname || '匿名用户'} />
            <Text style={styles.gridUsername} numberOfLines={1}>{author?.nickname || '匿名用户'}</Text>
          </TouchableOpacity>
          <View style={styles.gridLikeArea}>
            <MaterialCommunityIcons name="heart-outline" size={14} color={colors.text.secondary} />
            <Text style={styles.gridLikeCount}>{formatNumber(post.likes_count || 0)}</Text>
          </View>
        </View>
      </TouchableOpacity>
    );
  };

  return (
    showGrid ? renderGridCard() : (
    <TouchableOpacity
      activeOpacity={0.9}
      onPress={handleCardPress}
      style={[styles.card, style]}
    >
      <View style={styles.header}>
        <TouchableOpacity onPress={handleUserPress} activeOpacity={0.8} style={styles.authorWrap}>
          <Avatar source={author?.avatar} size={showGrid ? 22 : 36} name={author?.nickname || '匿名用户'} />
          <View style={styles.authorMeta}>
            <Text style={styles.authorName} numberOfLines={1}>{author?.nickname || '匿名用户'}</Text>
            {!showGrid && (
              <View style={styles.metaRow}>
                <PostCardRelativeTime createdAt={post.created_at} style={styles.timeText} />
                {post.is_pinned && (
                  <View style={styles.pinnedTag}>
                    <MaterialCommunityIcons name="pin" size={10} color={colors.warning.main} />
                    <Text style={styles.pinnedText}>置顶</Text>
                  </View>
                )}
                {isAuthor && <Text style={styles.badgeText}>楼主</Text>}
              </View>
            )}
          </View>
        </TouchableOpacity>
        {isPostAuthor && (
          <TouchableOpacity onPress={handleDelete} hitSlop={8} style={styles.deleteButton} disabled={isDeleting}>
            <MaterialCommunityIcons
              name={isDeleting ? 'loading' : 'delete-outline'}
              size={isDesktop ? 20 : 18}
              color={colors.text.hint}
            />
          </TouchableOpacity>
        )}
      </View>

      {!!post.title && (
        <Text style={styles.title} numberOfLines={isCompact ? 2 : undefined}>
          {post.title}
        </Text>
      )}

      {!isCompact && !!content && (
        <>
          <Text
            style={styles.content}
            numberOfLines={isExpanded ? undefined : contentNumberOfLines}
          >
            {content}
          </Text>
          {shouldTruncate && (
            <TouchableOpacity onPress={() => setIsExpanded((prev) => !prev)} style={styles.expandBtn}>
              <Text style={styles.expandText}>{isExpanded ? '收起' : '展开全文'}</Text>
            </TouchableOpacity>
          )}
        </>
      )}

      {renderImages()}
      {renderTopComment()}

      <View style={styles.actions}>
        <View style={styles.actionsLeading}>
          <View style={styles.viewsWrap}>
            <MaterialCommunityIcons name="eye-outline" size={16} color={colors.text.hint} />
            <Text style={styles.viewsText}>{post.views_count || 0}</Text>
          </View>
          {!!post.channel?.name && (
            <View style={[styles.channelTag, styles.channelTagAfterViews]}>
              <MaterialCommunityIcons name="tag-outline" size={11} color={colors.primary.main} />
              <Text style={styles.channelTagText} numberOfLines={1}>
                {post.channel.name}
              </Text>
            </View>
          )}
        </View>
        <View style={styles.actionButtons}>
          <TouchableOpacity style={styles.actionBtn} onPress={handleLike}>
            <MaterialCommunityIcons
              name={post.is_liked ? 'heart' : 'heart-outline'}
              size={18}
              color={post.is_liked ? colors.error.main : colors.text.secondary}
            />
            <Text style={post.is_liked ? styles.activeActionTextMerged : styles.actionText}>
              {post.likes_count > 0 ? formatNumber(post.likes_count) : '赞'}
            </Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionBtn} onPress={handleComment}>
            <MaterialCommunityIcons name="comment-outline" size={18} color={colors.text.secondary} />
            <Text style={styles.actionText}>{post.comments_count > 0 ? formatNumber(post.comments_count) : '评论'}</Text>
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionBtn} onPress={handleShare}>
            <MaterialCommunityIcons name="share-outline" size={18} color={colors.text.secondary} />
          </TouchableOpacity>
          <TouchableOpacity style={styles.actionBtn} onPress={handleBookmark}>
            <MaterialCommunityIcons
              name={post.is_favorited ? 'bookmark' : 'bookmark-outline'}
              size={18}
              color={post.is_favorited ? colors.warning.main : colors.text.secondary}
            />
          </TouchableOpacity>
        </View>
      </View>
    </TouchableOpacity>
    )
  );
};

PostCardInner.displayName = 'PostCard';

const PostCard = memo(PostCardInner, arePostCardPropsEqual);

export default PostCard;