import { callCloud, canUseCloud } from './cloud'
import { resolveCloudFileUrlMap } from './cloud-file'
import { mockPosts } from './mock'
import { emitPostLike, markStale } from '@/store/dataBus'
import type { FeedChannel, Post, PostComment, Visibility } from '@/types/domain'
import type { CursorResponse } from '@/types/cloud'

interface FeedListParams {
  channel: FeedChannel
  topic?: string
  cursor?: string
  pageSize?: number
  keyword?: string
}

export interface FeedListResponse extends CursorResponse<Post> {
  topics?: string[]
}

interface UserPostsParams {
  authorId?: string
  cursor?: string
  pageSize?: number
}

interface PostDetailParams {
  postId: string
  commentCursor?: string
  commentPageSize?: number
}

export interface PostDetailResponse {
  post: Post | null
  comments: PostComment[]
  commentsNextCursor?: string
}

function matchesKeyword(post: Post, keyword?: string): boolean {
  const query = keyword?.trim().toLowerCase()
  if (!query) return true

  return [
    post.body,
    post.locationText,
    post.author.name,
    post.pet.name,
    post.pet.breed,
    ...(post.topics || [])
  ]
    .filter(Boolean)
    .join(' ')
    .toLowerCase()
    .includes(query)
}

function getMockTopics(posts: Post[]): string[] {
  const counts = new Map<string, number>()
  posts.forEach(post => {
    post.topics.forEach(topic => counts.set(topic, (counts.get(topic) || 0) + 1))
  })

  return [...counts.entries()]
    .sort((a, b) => b[1] - a[1])
    .map(([topic]) => topic)
}

async function resolvePostAssets(posts: Post[]): Promise<Post[]> {
  const urls = posts.flatMap(post => [
    post.author.avatarUrl || '',
    ...(post.media || [])
  ])
  const urlMap = await resolveCloudFileUrlMap(urls)
  const readUrl = (url?: string) => {
    if (!url) return url
    return urlMap.has(url) ? urlMap.get(url) || '' : url
  }

  return posts.map(post => ({
    ...post,
    author: {
      ...post.author,
      avatarUrl: readUrl(post.author.avatarUrl)
    },
    media: post.media
      ? post.media.map(url => readUrl(url) || '').filter(Boolean)
      : post.media
  }))
}

async function resolveCommentAssets(comments: PostComment[]): Promise<PostComment[]> {
  const urlMap = await resolveCloudFileUrlMap(comments.map(comment => comment.author.avatarUrl || ''))
  const readUrl = (url?: string) => {
    if (!url) return url
    return urlMap.has(url) ? urlMap.get(url) || '' : url
  }

  return comments.map(comment => ({
    ...comment,
    author: {
      ...comment.author,
      avatarUrl: readUrl(comment.author.avatarUrl)
    }
  }))
}

export async function getUserPosts(params: UserPostsParams | string = {}): Promise<CursorResponse<Post>> {
  const request = typeof params === 'string' ? { authorId: params } : params
  const pageSize = request.pageSize || 20
  if (!canUseCloud()) {
    const start = Math.max(0, Number(request.cursor || 0))
    const list = mockPosts.slice(start, start + pageSize)
    const nextCursor = start + pageSize < mockPosts.length ? String(start + pageSize) : ''
    return { list, nextCursor }
  }

  const result = await callCloud<UserPostsParams, CursorResponse<Post>>('userPosts', request)
  return {
    list: await resolvePostAssets(result.list || []),
    nextCursor: result.nextCursor || ''
  }
}

export async function getPostDetail(
  params: string | PostDetailParams
): Promise<PostDetailResponse> {
  const request = typeof params === 'string' ? { postId: params } : params
  if (!canUseCloud()) {
    return { post: mockPosts.find(item => item._id === request.postId) || null, comments: [] }
  }
  const result = await callCloud<PostDetailParams, PostDetailResponse>(
    'postDetail',
    request
  )
  const [post] = result.post ? await resolvePostAssets([result.post]) : [null]
  const comments = await resolveCommentAssets(result.comments || [])
  return { post, comments, commentsNextCursor: result.commentsNextCursor || '' }
}

export async function getFeedList(params: FeedListParams): Promise<FeedListResponse> {
  const { channel, topic, cursor, pageSize = 20, keyword } = params

  // Only fall back to sample posts in non-cloud previews (e.g. H5 dev).
  if (!canUseCloud()) {
    const filteredPosts = mockPosts
      .filter(post => !topic || post.topics.includes(topic))
      .filter(post => matchesKeyword(post, keyword))
    const start = Math.max(0, Number(cursor || 0))
    const list = filteredPosts.slice(start, start + pageSize)
    const nextCursor = start + pageSize < filteredPosts.length ? String(start + pageSize) : ''
    return { list, nextCursor, topics: getMockTopics(mockPosts) }
  }

  // In the real mini program always return cloud data — never mask it with
  // sample posts, even when the feed is empty or the call fails.
  const result = await callCloud<FeedListParams, FeedListResponse>('feedList', {
    channel,
    topic,
    cursor,
    pageSize,
    keyword
  })
  const list = result.list || []
  const filteredList = keyword ? list.filter(post => matchesKeyword(post, keyword)) : list

  return {
    list: await resolvePostAssets(filteredList),
    nextCursor: result.nextCursor || '',
    topics: result.topics || []
  }
}

export async function setPostLike(postId: string, liked: boolean): Promise<{ liked: boolean; likes: number }> {
  if (!canUseCloud()) {
    const post = mockPosts.find(item => item._id === postId)
    const result = { liked, likes: Math.max(0, (post?.counts.likes || 0) + (liked ? 1 : -1)) }
    emitPostLike({ postId, ...result })
    return result
  }

  const result = await callCloud<{ postId: string; liked: boolean }, { liked: boolean; likes: number }>(
    'postLike',
    { postId, liked }
  )
  // Broadcast the confirmed state so every page showing this post stays in sync
  // without a full reload.
  emitPostLike({ postId, liked: result.liked, likes: result.likes })
  return result
}

export async function deletePost(postId: string): Promise<void> {
  if (!canUseCloud()) {
    markStale('feed', 'myPosts', 'profile')
    return
  }
  await callCloud('postDelete', { postId })
  // Removed from the feed, my posts and the author's post count.
  markStale('feed', 'myPosts', 'profile')
}

export async function updatePost(
  postId: string,
  patch: { content?: string; visibility?: Visibility }
): Promise<void> {
  if (!canUseCloud()) {
    markStale('feed', 'myPosts')
    return
  }
  await callCloud('postUpdate', { postId, ...patch })
  markStale('feed', 'myPosts')
}

export async function createPostComment(postId: string, content: string): Promise<{ commentId: string; comments: number }> {
  if (!canUseCloud()) {
    const post = mockPosts.find(item => item._id === postId)
    return { commentId: `local_comment_${Date.now()}`, comments: (post?.counts.comments || 0) + 1 }
  }

  return callCloud('commentCreate', { postId, content })
}
