import type { DefaultDocumentIDType, Where } from 'payload'

import configPromise from '@payload-config'
import { ArrowRight, BadgeCheck, CalendarDays, Clock3, Leaf, UserRound } from 'lucide-react'
import { draftMode } from 'next/headers'
import Link from 'next/link'
import { getPayload } from 'payload'
import React from 'react'

import { Media } from '@/components/Media'
import type {
  LatestPostsBlock as LatestPostsBlockProps,
  Media as MediaType,
  Post,
} from '@/payload-types'
import { cn } from '@/utilities/cn'

type RelationshipValue = { id?: number | string } | number | string | null | undefined
type LexicalNode = {
  children?: LexicalNode[]
  root?: LexicalNode
  text?: string
}

const getRelationshipID = (value: RelationshipValue): number | string | null => {
  if (typeof value === 'string' || typeof value === 'number') {
    return value
  }

  if (value && typeof value === 'object') {
    return typeof value.id === 'string' || typeof value.id === 'number' ? value.id : null
  }

  return null
}

const isMediaResource = (value: unknown): value is MediaType =>
  value !== null && typeof value === 'object' && 'url' in value

const isPost = (value: unknown): value is Post =>
  value !== null && typeof value === 'object' && 'slug' in value && 'title' in value

const resolveLimit = (limit?: number | null): number => {
  if (typeof limit !== 'number' || !Number.isFinite(limit)) {
    return 6
  }

  return Math.min(Math.max(Math.floor(limit), 1), 6)
}

const getPostCategoryLabel = (post: Post): string => {
  const category = post.categories?.find(
    (candidate) => typeof candidate === 'object' && candidate !== null && candidate.title,
  )

  return typeof category === 'object' && category?.title ? category.title : 'Artikel'
}

const getPostImage = (post: Post): MediaType | null => {
  if (isMediaResource(post.heroImage)) {
    return post.heroImage
  }

  if (isMediaResource(post.meta?.image)) {
    return post.meta.image
  }

  return null
}

const getPostAuthor = (post: Post, fallback?: string | null): string => {
  const author = post.populatedAuthors?.find((candidate) => candidate?.name)

  return author?.name || fallback || 'Tim Bunga Mekarsari'
}

const countWordsFromLexical = (value: unknown): number => {
  if (!value || typeof value !== 'object') {
    return 0
  }

  const node = value as LexicalNode
  const rootWords = node.root ? countWordsFromLexical(node.root) : 0
  const ownWords = node.text?.trim() ? node.text.trim().split(/\s+/).length : 0
  const childWords = Array.isArray(node.children)
    ? node.children.reduce((total, child) => total + countWordsFromLexical(child), 0)
    : 0

  return rootWords + ownWords + childWords
}

const getPostReadTime = (post: Post, wordsPerMinute?: number | null): string | null => {
  const wordCount = countWordsFromLexical((post as { content?: unknown }).content)

  if (wordCount <= 0) {
    return null
  }

  const resolvedWordsPerMinute =
    typeof wordsPerMinute === 'number' && Number.isFinite(wordsPerMinute) && wordsPerMinute > 0
      ? wordsPerMinute
      : 180

  return `${Math.max(1, Math.ceil(wordCount / resolvedWordsPerMinute))} mnt`
}

const formatPostDate = (date?: null | string): string | null => {
  if (!date) {
    return null
  }

  return new Intl.DateTimeFormat('id-ID', {
    day: '2-digit',
    month: 'short',
    year: 'numeric',
  }).format(new Date(date))
}

export const LatestPostsBlock: React.FC<
  LatestPostsBlockProps & {
    id?: DefaultDocumentIDType
    className?: string
  }
> = async (props) => {
  const {
    authorFallback,
    authorRoleFallback,
    categories,
    className,
    ctaLabel,
    ctaUrl,
    description,
    eyebrow,
    heading,
    id,
    limit: limitFromProps,
    populateBy,
    readingWordsPerMinute,
    selectedDocs,
    verificationNote,
  } = props
  const limit = resolveLimit(limitFromProps)
  const resolvedAuthorRole = authorRoleFallback || 'Tim Botani'
  const { isEnabled: draft } = await draftMode()
  const payload = await getPayload({ config: configPromise })

  let posts: Post[] = []

  if (populateBy === 'selection' && selectedDocs?.length) {
    const selectedIDs = selectedDocs
      .map((post) => getRelationshipID(post))
      .filter((value): value is number | string => value !== null)
      .slice(0, limit)

    if (selectedIDs.length > 0) {
      const fetchedPosts = await payload.find({
        collection: 'posts',
        depth: 1,
        draft,
        limit: selectedIDs.length,
        overrideAccess: draft,
        pagination: false,
        where: {
          id: {
            in: selectedIDs,
          },
        },
      })
      const postsByID = new Map(fetchedPosts.docs.map((post) => [String(post.id), post]))

      posts = selectedIDs
        .map((id) => postsByID.get(String(id)))
        .filter((post): post is Post => Boolean(post))
    } else {
      posts = selectedDocs.filter(isPost).slice(0, limit)
    }
  } else {
    const categoryIDs =
      categories
        ?.map((category) => getRelationshipID(category))
        .filter((value): value is number | string => value !== null) ?? []
    const categoryWhere: Where | undefined =
      categoryIDs.length > 0
        ? {
            categories: {
              in: categoryIDs,
            },
          }
        : undefined

    const fetchedPosts = await payload.find({
      collection: 'posts',
      depth: 1,
      draft,
      limit,
      overrideAccess: draft,
      pagination: false,
      sort: '-publishedAt',
      ...(categoryWhere ? { where: categoryWhere } : {}),
    })

    posts = fetchedPosts.docs
  }

  if (!posts.length) {
    return null
  }

  return (
    <section className={cn('py-16 md:py-24', className)} id={id ? `block-${id}` : undefined}>
      <div className="mx-auto max-w-7xl px-4 md:px-16">
        <div className="mb-12 flex flex-col gap-8 md:mb-16 md:flex-row md:items-end md:justify-between">
          <div className="max-w-2xl space-y-4">
            {eyebrow ? (
              <span className="block text-xs font-bold uppercase tracking-[0.24em] text-primary">
                {eyebrow}
              </span>
            ) : null}
            {heading ? (
              <h2 className="font-serif text-3xl font-bold leading-tight text-primary md:text-5xl">
                {heading}
              </h2>
            ) : null}
            {description ? (
              <p className="max-w-xl text-base leading-relaxed text-muted-foreground md:text-lg">
                {description}
              </p>
            ) : null}
          </div>

          {ctaUrl && ctaLabel ? (
            <Link
              className="group inline-flex items-center justify-center gap-3 rounded-full bg-primary px-7 py-4 text-sm font-bold text-primary-foreground transition-all hover:bg-primary/90 hover:shadow-lg"
              href={ctaUrl}
            >
              <span className="border-b border-primary-foreground/30 transition-colors group-hover:border-primary-foreground">
                {ctaLabel}
              </span>
              <ArrowRight className="h-5 w-5 transition-transform group-hover:translate-x-1" />
            </Link>
          ) : null}
        </div>

        <div className="grid grid-cols-1 gap-8 md:grid-cols-3">
          {posts.map((post, index) => {
            const image = getPostImage(post)
            const categoryLabel = getPostCategoryLabel(post)
            const author = getPostAuthor(post, authorFallback)
            const publishedAt = formatPostDate(post.publishedAt)
            const readTime = getPostReadTime(post, readingWordsPerMinute)

            return (
              <article
                className="group flex h-full flex-col overflow-hidden rounded-xl border border-border/40 bg-white transition-all hover:shadow-lg dark:bg-card"
                key={post.id ?? `${post.slug}-${index}`}
              >
                <Link className="flex h-full flex-col" href={`/posts/${post.slug}`}>
                  <div className="relative aspect-[4/3] overflow-hidden bg-secondary">
                    {image ? (
                      <Media
                        alt={post.title}
                        fill
                        htmlElement={null}
                        imgClassName="object-cover transition-transform duration-700 group-hover:scale-105"
                        resource={image}
                        size="(max-width: 768px) 100vw, 33vw"
                      />
                    ) : (
                      <div className="flex h-full w-full items-center justify-center bg-gradient-to-br from-primary/10 via-secondary to-primary/5">
                        <Leaf className="h-12 w-12 text-primary/50" />
                      </div>
                    )}
                    <div className="absolute left-3 top-3 rounded-full bg-white/90 px-2.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-primary shadow-sm backdrop-blur-sm">
                      {categoryLabel}
                    </div>
                  </div>

                  <div className="flex flex-1 flex-col p-5">
                    <div className="mb-3 flex flex-wrap items-center gap-3 text-[11px] font-medium text-muted-foreground/70">
                      {publishedAt ? (
                        <span className="flex items-center gap-1">
                          <CalendarDays className="h-3.5 w-3.5" />
                          {publishedAt}
                        </span>
                      ) : null}
                      {readTime ? (
                        <span className="flex items-center gap-1">
                          <Clock3 className="h-3.5 w-3.5" />
                          {readTime}
                        </span>
                      ) : null}
                    </div>

                    <h3 className="mb-4 text-[18px] font-bold leading-snug text-primary transition-colors group-hover:text-secondary">
                      {post.title}
                    </h3>

                    {post.meta?.description ? (
                      <p className="mb-4 line-clamp-2 text-sm leading-relaxed text-muted-foreground/80">
                        {post.meta.description}
                      </p>
                    ) : null}

                    <div className="mt-auto border-t border-border/20 pt-4">
                      <div className="flex items-center justify-between gap-4">
                        <div className="flex min-w-0 items-center gap-2">
                          <div className="flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full border border-primary/5 bg-primary/10 text-primary">
                            <UserRound className="h-4 w-4" />
                          </div>
                          <div className="min-w-0">
                            <p className="truncate text-xs font-bold leading-none text-primary">
                              {author}
                            </p>
                            {resolvedAuthorRole ? (
                              <p className="mt-1 truncate text-[10px] text-muted-foreground/70">
                                {resolvedAuthorRole}
                              </p>
                            ) : null}
                          </div>
                        </div>
                        <BadgeCheck className="h-4 w-4 shrink-0 text-secondary" />
                      </div>

                      {verificationNote ? (
                        <p className="mt-3 text-[9px] italic leading-tight text-muted-foreground/60">
                          {verificationNote}
                        </p>
                      ) : null}
                    </div>
                  </div>
                </Link>
              </article>
            )
          })}
        </div>
      </div>
    </section>
  )
}
