import Taro from '@tarojs/taro'
import { callCloud, canUseCloud } from './cloud'
import { isCloudFileId, resolveCloudFileUrlMap } from './cloud-file'
import { markStale } from '@/store/dataBus'
import type { MediaAsset, PublishDraft } from '@/types/domain'

export class UploadPostMediaError extends Error {
  failedItems: MediaAsset[]

  constructor(failedItems: MediaAsset[]) {
    super('图片上传失败')
    this.name = 'UploadPostMediaError'
    this.failedItems = failedItems
  }
}

export function isUploadPostMediaError(error: unknown): error is UploadPostMediaError {
  return error instanceof UploadPostMediaError || (
    typeof error === 'object' &&
    error !== null &&
    (error as { name?: string }).name === 'UploadPostMediaError' &&
    Array.isArray((error as { failedItems?: unknown }).failedItems)
  )
}

export async function chooseImages(currentCount: number): Promise<MediaAsset[]> {
  const remain = Math.max(0, 9 - currentCount)
  if (remain <= 0) return []

  const result = await Taro.chooseMedia({
    count: Math.min(3, remain),
    mediaType: ['image'],
    sourceType: ['album', 'camera']
  })

  return result.tempFiles.map((file, index) => ({
    id: `${Date.now()}_${index}`,
    url: file.tempFilePath,
    type: 'image'
  }))
}

async function resolveDraftMedia(draft: PublishDraft | null): Promise<PublishDraft | null> {
  if (!draft || !Array.isArray(draft.media)) return draft

  const fileIds = draft.media
    .map(item => item.fileId || item.url || '')
    .filter(isCloudFileId)
  const urlMap = await resolveCloudFileUrlMap(fileIds)

  return {
    ...draft,
    media: draft.media.map(item => {
      const fileId = isCloudFileId(item.fileId) ? item.fileId : isCloudFileId(item.url) ? item.url : item.fileId
      if (!fileId || !isCloudFileId(fileId)) return item
      return {
        ...item,
        fileId,
        url: urlMap.get(fileId) || ''
      }
    })
  }
}

// Upload any locally-chosen images to cloud storage so they persist and are
// viewable by others. Items that are already cloud fileIDs are left untouched.
export async function uploadPostMedia(media: MediaAsset[]): Promise<MediaAsset[]> {
  if (!canUseCloud()) return media
  const results = await Promise.all(
    media.map(async item => {
      if (isCloudFileId(item.fileId)) return { item: { ...item, url: item.fileId } }
      if (!item.url || isCloudFileId(item.url)) return { item: { ...item, fileId: item.url, url: item.url } }
      try {
        const ext = (item.url.split('?')[0].split('.').pop() || 'jpg').slice(0, 5)
        const cloudPath = `posts/${Date.now()}_${Math.random().toString(36).slice(2, 8)}.${ext}`
        const res = await Taro.cloud.uploadFile({ cloudPath, filePath: item.url })
        return { item: { ...item, url: res.fileID, fileId: res.fileID } }
      } catch {
        return { item, failed: true }
      }
    })
  )

  const failedItems = results.filter(result => result.failed).map(result => result.item)
  if (failedItems.length > 0) {
    throw new UploadPostMediaError(failedItems)
  }

  return results.map(result => result.item)
}

export async function createPost(draft: PublishDraft): Promise<{ postId: string }> {
  const result = canUseCloud()
    ? await callCloud<PublishDraft, { postId: string }>('postCreate', draft)
    : { postId: `local_${Date.now()}` }
  // New post — the plaza feed and "my posts" should refresh on return.
  markStale('feed', 'myPosts', 'profile')
  return result
}

export async function saveDraft(draft: PublishDraft): Promise<{ draftId: string; updatedAt: number }> {
  if (!canUseCloud()) return { draftId: 'local_draft', updatedAt: Date.now() }
  return callCloud('draftSave', { draft })
}

export async function getDraft(): Promise<{ draft: PublishDraft | null; updatedAt?: number }> {
  if (!canUseCloud()) return { draft: null }
  const result = await callCloud<object, { draft: PublishDraft | null; updatedAt?: number }>('draftGet', {})
  return { ...result, draft: await resolveDraftMedia(result.draft) }
}
