export type BlogPost = {
  id?: string
  path?: string
  title?: string
  description?: string
  published?: string
  updated?: string | null
  image?: string | null
  draft?: boolean
  pinned?: boolean
  tags?: string[]
  series?: string | null
  seriesOrder?: number | null
  difficulty?: string | null
  body?: unknown
}

export type ArchiveMonth = {
  month: string
  label: string
  posts: BlogPost[]
}

export type ArchiveYear = {
  year: string
  months: ArchiveMonth[]
}

export type SeriesSummary = {
  name: string
  path: string
  count: number
  posts: BlogPost[]
  latestPost: BlogPost
  latestTime: number
  tags: string[]
}

export function isVisiblePost(post: BlogPost): boolean {
  return !post.draft
}

export function sortPostsByPublished(posts: BlogPost[]): BlogPost[] {
  return [...posts].sort((a, b) => getPostTime(b.published) - getPostTime(a.published))
}

export function sortPostsForSeries(posts: BlogPost[]): BlogPost[] {
  return [...posts].sort((a, b) => {
    const orderA = typeof a.seriesOrder === 'number' ? a.seriesOrder : Number.MAX_SAFE_INTEGER
    const orderB = typeof b.seriesOrder === 'number' ? b.seriesOrder : Number.MAX_SAFE_INTEGER
    if (orderA !== orderB) return orderA - orderB
    return getPostTime(a.published) - getPostTime(b.published)
  })
}

export function getArchiveGroups(posts: BlogPost[]): ArchiveYear[] {
  const years = new Map<string, Map<string, BlogPost[]>>()

  for (const post of sortPostsByPublished(posts)) {
    const date = getPostDate(post.published)
    if (!date) continue

    const year = String(date.getFullYear())
    const month = String(date.getMonth() + 1).padStart(2, '0')

    if (!years.has(year)) {
      years.set(year, new Map())
    }

    const months = years.get(year)!
    if (!months.has(month)) {
      months.set(month, [])
    }

    months.get(month)!.push(post)
  }

  return [...years.entries()]
    .sort(([a], [b]) => Number(b) - Number(a))
    .map(([year, months]) => ({
      year,
      months: [...months.entries()]
        .sort(([a], [b]) => Number(b) - Number(a))
        .map(([month, monthPosts]) => ({
          month,
          label: `${Number(month)} 月`,
          posts: monthPosts,
        })),
    }))
}

export function getSeriesSummaries(posts: BlogPost[]): SeriesSummary[] {
  const groups = new Map<string, BlogPost[]>()

  for (const post of posts) {
    const name = normalizeSeriesName(post.series)
    if (!name) continue

    if (!groups.has(name)) {
      groups.set(name, [])
    }
    groups.get(name)!.push(post)
  }

  return [...groups.entries()]
    .map(([name, items]) => {
      const seriesPosts = sortPostsForSeries(items)
      const latestPost = sortPostsByPublished(seriesPosts)[0]
      const tags = [...new Set(seriesPosts.flatMap(post => post.tags || []))].slice(0, 6)

      return {
        name,
        path: getSeriesPath(name),
        count: seriesPosts.length,
        posts: seriesPosts,
        latestPost,
        latestTime: Math.max(...seriesPosts.map(post => getPostTime(post.updated || post.published))),
        tags,
      }
    })
    .sort((a, b) => b.latestTime - a.latestTime)
}

export function normalizeSeriesName(series?: string | null): string {
  return typeof series === 'string' ? series.trim() : ''
}

export function getSeriesPath(name: string): string {
  return `/series/${encodeURIComponent(name)}`
}

export function decodeRouteParam(value: string): string {
  try {
    return decodeURIComponent(value)
  } catch {
    return value
  }
}

export function formatPostDate(date?: string | null): string {
  if (!date) return ''

  return new Date(date).toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  })
}

export function getWordCount(post: BlogPost): number {
  const text = JSON.stringify(post.body || '').replace(/<[^>]*>/g, '')
  const chinese = text.match(/[一-龥]/g) || []
  const english = text.match(/[a-zA-Z]+/g) || []
  return chinese.length + english.length
}

export function getReadTime(post: BlogPost): number {
  return Math.max(1, Math.ceil(getWordCount(post) / 300))
}

function getPostDate(date?: string | null): Date | null {
  if (!date) return null

  const value = new Date(date)
  if (Number.isNaN(value.getTime())) return null
  return value
}

function getPostTime(date?: string | null): number {
  return getPostDate(date)?.getTime() || 0
}
