import { normalizeMarkdown } from '@/lib/markdown-diff'

export type ParsedMarkdownMeta = {
  title: string
  slug: string
  description: string
  category: string
  postType: string
  image: string
  images: string[]
  pinned: boolean
  status: string
  visibility: string
  publishAt: string
  unpublishAt: string
  canonicalUrl: string
  noindex: boolean
  ogImage: string
  redirectFrom: string[]
  redirectTo: string
  tags: string[]
}

export type ParsedMarkdownDocument = {
  meta: ParsedMarkdownMeta
  body: string
  markdown: string
}

const defaultMeta: ParsedMarkdownMeta = {
  title: '',
  slug: '',
  description: '',
  category: '',
  postType: 'article',
  image: '',
  images: [],
  pinned: false,
  status: 'published',
  visibility: 'public',
  publishAt: '',
  unpublishAt: '',
  canonicalUrl: '',
  noindex: false,
  ogImage: '',
  redirectFrom: [],
  redirectTo: '',
  tags: [],
}

function parseScalar(value: string) {
  const trimmed = value.trim()
  if (!trimmed) {
    return ''
  }

  if (
    trimmed.startsWith('"') ||
    trimmed.startsWith("'") ||
    trimmed.startsWith('[') ||
    trimmed.startsWith('{')
  ) {
    try {
      return JSON.parse(trimmed)
    } catch {
      return trimmed.replace(/^['"]|['"]$/g, '')
    }
  }

  if (trimmed === 'true') {
    return true
  }

  if (trimmed === 'false') {
    return false
  }

  return trimmed
}

function toStringList(value: unknown) {
  if (Array.isArray(value)) {
    return value
      .map((item) => String(item).trim())
      .filter(Boolean)
  }

  if (typeof value === 'string') {
    return value
      .split(/[，,]/)
      .map((item) => item.trim())
      .filter(Boolean)
  }

  return []
}

export function parseMarkdownDocument(markdown: string): ParsedMarkdownDocument {
  const normalized = normalizeMarkdown(markdown)
  const meta: ParsedMarkdownMeta = { ...defaultMeta }

  if (!normalized.startsWith('---\n')) {
    return {
      meta,
      body: normalized.trimStart(),
      markdown: normalized,
    }
  }

  const endIndex = normalized.indexOf('\n---\n', 4)
  if (endIndex === -1) {
    return {
      meta,
      body: normalized.trimStart(),
      markdown: normalized,
    }
  }

  const frontmatter = normalized.slice(4, endIndex)
  const body = normalized.slice(endIndex + 5).trimStart()
  let currentListKey: 'tags' | 'images' | 'categories' | 'redirect_from' | null = null
  const categories: string[] = []

  frontmatter.split('\n').forEach((line) => {
    const listItemMatch = line.match(/^\s*-\s*(.+)\s*$/)
    if (listItemMatch && currentListKey) {
      const parsed = parseScalar(listItemMatch[1])
      const nextValue = typeof parsed === 'string' ? parsed.trim() : String(parsed).trim()
      if (!nextValue) {
        return
      }

      if (currentListKey === 'tags') {
        meta.tags.push(nextValue)
      } else if (currentListKey === 'images') {
        meta.images.push(nextValue)
      } else if (currentListKey === 'redirect_from') {
        meta.redirectFrom.push(nextValue)
      } else {
        categories.push(nextValue)
      }
      return
    }

    currentListKey = null

    const keyMatch = line.match(/^([A-Za-z_]+):\s*(.*)$/)
    if (!keyMatch) {
      return
    }

    const [, rawKey, rawValue] = keyMatch
    const key = rawKey.trim()
    const value = parseScalar(rawValue)

    if (key === 'tags') {
      const tags = toStringList(value)
      if (tags.length) {
        meta.tags = tags
      } else if (!String(rawValue).trim()) {
        currentListKey = 'tags'
      }
      return
    }

    if (key === 'images') {
      const images = toStringList(value)
      if (images.length) {
        meta.images = images
      } else if (!String(rawValue).trim()) {
        currentListKey = 'images'
      }
      return
    }

    if (key === 'redirect_from') {
      const redirectFrom = toStringList(value)
      if (redirectFrom.length) {
        meta.redirectFrom = redirectFrom
      } else if (!String(rawValue).trim()) {
        currentListKey = 'redirect_from'
      }
      return
    }

    if (key === 'categories' || key === 'category') {
      const parsedCategories = toStringList(value)
      if (parsedCategories.length) {
        categories.push(...parsedCategories)
      } else if (!String(rawValue).trim()) {
        currentListKey = 'categories'
      }
      return
    }

    switch (key) {
      case 'title':
        meta.title = String(value).trim()
        break
      case 'slug':
        meta.slug = String(value).trim()
        break
      case 'description':
        meta.description = String(value).trim()
        break
      case 'post_type':
        meta.postType = String(value).trim() || 'article'
        break
      case 'image':
        meta.image = String(value).trim()
        break
      case 'pinned':
        meta.pinned = Boolean(value)
        break
      case 'status':
        meta.status = String(value).trim() || 'published'
        break
      case 'visibility':
        meta.visibility = String(value).trim() || 'public'
        break
      case 'publish_at':
        meta.publishAt = String(value).trim()
        break
      case 'unpublish_at':
        meta.unpublishAt = String(value).trim()
        break
      case 'canonical_url':
        meta.canonicalUrl = String(value).trim()
        break
      case 'noindex':
        meta.noindex = Boolean(value)
        break
      case 'og_image':
        meta.ogImage = String(value).trim()
        break
      case 'redirect_to':
        meta.redirectTo = String(value).trim()
        break
      case 'published':
        meta.status = value === false ? 'draft' : 'published'
        break
      case 'draft':
        if (value === true) {
          meta.status = 'draft'
        }
        break
      default:
        break
    }
  })

  meta.category = categories[0] ?? meta.category

  return {
    meta,
    body,
    markdown: normalized,
  }
}

export function buildMarkdownDocument(meta: ParsedMarkdownMeta, body: string) {
  const lines = [
    '---',
    `title: ${JSON.stringify(meta.title.trim() || meta.slug || 'untitled-post')}`,
    `slug: ${meta.slug.trim() || 'untitled-post'}`,
  ]

  if (meta.description.trim()) {
    lines.push(`description: ${JSON.stringify(meta.description.trim())}`)
  }

  if (meta.category.trim()) {
    lines.push(`category: ${JSON.stringify(meta.category.trim())}`)
  }

  lines.push(`post_type: ${JSON.stringify(meta.postType.trim() || 'article')}`)
  lines.push(`pinned: ${meta.pinned ? 'true' : 'false'}`)
  lines.push(`status: ${JSON.stringify(meta.status.trim() || 'published')}`)
  lines.push(`visibility: ${JSON.stringify(meta.visibility.trim() || 'public')}`)
  lines.push(`noindex: ${meta.noindex ? 'true' : 'false'}`)

  if (meta.publishAt.trim()) {
    lines.push(`publish_at: ${JSON.stringify(meta.publishAt.trim())}`)
  }

  if (meta.unpublishAt.trim()) {
    lines.push(`unpublish_at: ${JSON.stringify(meta.unpublishAt.trim())}`)
  }

  if (meta.image.trim()) {
    lines.push(`image: ${JSON.stringify(meta.image.trim())}`)
  }

  if (meta.images.length) {
    lines.push('images:')
    meta.images.forEach((image) => {
      lines.push(`  - ${JSON.stringify(image)}`)
    })
  }

  if (meta.tags.length) {
    lines.push('tags:')
    meta.tags.forEach((tag) => {
      lines.push(`  - ${JSON.stringify(tag)}`)
    })
  }

  if (meta.canonicalUrl.trim()) {
    lines.push(`canonical_url: ${JSON.stringify(meta.canonicalUrl.trim())}`)
  }

  if (meta.ogImage.trim()) {
    lines.push(`og_image: ${JSON.stringify(meta.ogImage.trim())}`)
  }

  if (meta.redirectFrom.length) {
    lines.push('redirect_from:')
    meta.redirectFrom.forEach((item) => {
      lines.push(`  - ${JSON.stringify(item)}`)
    })
  }

  if (meta.redirectTo.trim()) {
    lines.push(`redirect_to: ${JSON.stringify(meta.redirectTo.trim())}`)
  }

  return `${lines.join('\n')}\n---\n\n${body.trim()}\n`
}
