'use client'

import { useMemo, useState } from 'react'
import { tagToSlug } from '@/lib/tags'
import { PostCard } from './PostCard'

type SearchPost = {
  slug: string
  title: string
  date: string
  excerpt: string
  tags: string[]
  author: string | null
  coverImage: string | null
  readingTime: number
}

type TagFilter = {
  slug: string
  label: string
  count: number
}

type ScoredPost = {
  post: SearchPost
  score: number
  index: number
}

interface PostSearchProps {
  posts: SearchPost[]
}

const normalizeSearchText = (value: string): string =>
  value
    .normalize('NFKD')
    .replace(/[\u0300-\u036f]/g, '')
    .toLowerCase()
    .trim()

const getOrderedCharacterScore = (field: string, query: string): number => {
  if (!query) return 0

  let queryIndex = 0
  let gaps = 0
  let lastMatchIndex = -1

  for (let fieldIndex = 0; fieldIndex < field.length && queryIndex < query.length; fieldIndex += 1) {
    if (field[fieldIndex] !== query[queryIndex]) continue

    if (lastMatchIndex >= 0) gaps += fieldIndex - lastMatchIndex - 1
    lastMatchIndex = fieldIndex
    queryIndex += 1
  }

  if (queryIndex !== query.length) return 0

  return Math.max(1, 80 - gaps - Math.max(0, field.length - query.length) * 0.05)
}

const scoreField = (field: string, query: string, weight: number): number => {
  if (!field || !query) return 0

  if (field === query) return 1000 * weight
  if (field.startsWith(query)) return 700 * weight + query.length
  if (field.includes(query)) return 500 * weight + query.length

  return getOrderedCharacterScore(field, query) * weight
}

const scorePost = (post: SearchPost, query: string): number => {
  if (!query) return 0

  const fields = [
    [post.title, 1.4],
    [post.slug, 1.1],
    [post.excerpt, 0.8],
    [post.author ?? '', 0.75],
    [post.tags.join(' '), 1],
  ] as const

  return fields.reduce(
    (score, [field, weight]) => score + scoreField(normalizeSearchText(field), query, weight),
    0
  )
}

const getTagFilters = (posts: SearchPost[]): TagFilter[] => {
  const tags = new Map<string, TagFilter>()

  posts.forEach((post) => {
    const postTags = new Set<string>()

    post.tags.forEach((tag) => {
      const slug = tagToSlug(tag)
      if (!slug || postTags.has(slug)) return

      postTags.add(slug)
      const existing = tags.get(slug)

      if (existing) {
        existing.count += 1
        if (tag.localeCompare(existing.label) < 0) existing.label = tag
        return
      }

      tags.set(slug, { slug, label: tag, count: 1 })
    })
  })

  return Array.from(tags.values()).sort(
    (a, b) => a.label.localeCompare(b.label) || a.slug.localeCompare(b.slug)
  )
}

export function PostSearch({ posts }: PostSearchProps) {
  const [query, setQuery] = useState('')
  const [selectedTag, setSelectedTag] = useState<string | null>(null)

  const tagFilters = useMemo(() => getTagFilters(posts), [posts])
  const normalizedQuery = normalizeSearchText(query)

  const results = useMemo(() => {
    const scoredPosts = posts.reduce<ScoredPost[]>((matches, post, index) => {
      if (selectedTag && !post.tags.some((tag) => tagToSlug(tag) === selectedTag)) {
        return matches
      }

      const score = scorePost(post, normalizedQuery)
      if (normalizedQuery && score <= 0) return matches

      matches.push({ post, score, index })
      return matches
    }, [])

    return scoredPosts
      .sort((a, b) => b.score - a.score || a.index - b.index)
      .map(({ post }) => post)
  }, [normalizedQuery, posts, selectedTag])

  if (posts.length === 0) {
    return <p className="text-ink-soft">No posts yet.</p>
  }

  const hasFilters = Boolean(normalizedQuery || selectedTag)
  const selectedTagLabel = tagFilters.find((tag) => tag.slug === selectedTag)?.label

  return (
    <section aria-labelledby="posts-search-heading" className="space-y-8">
      <div className="space-y-5 rounded-xl border border-border bg-surface/30 p-4 sm:p-5">
        <div className="space-y-2">
          <label id="posts-search-heading" htmlFor="post-search" className="block text-sm font-medium text-ink">
            Search posts
          </label>
          <div className="flex flex-col gap-2 sm:flex-row">
            <input
              id="post-search"
              type="search"
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder="Search title, excerpt, tag, author, or slug"
              className="min-w-0 flex-1 rounded-lg border border-border bg-canvas px-3 py-2 text-sm text-ink outline-none transition-colors placeholder:text-ink-soft/70 focus:border-accent focus:ring-2 focus:ring-accent/20"
            />
            <button
              type="button"
              onClick={() => setQuery('')}
              disabled={!query}
              className="rounded-lg border border-border px-3 py-2 text-sm text-ink-soft transition-colors hover:border-accent/50 hover:text-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent disabled:cursor-not-allowed disabled:opacity-50"
            >
              Clear search
            </button>
          </div>
        </div>

        {tagFilters.length > 0 && (
          <div className="space-y-2" aria-label="Filter posts by tag">
            <p className="text-sm font-medium text-ink">Filter by tag</p>
            <div className="flex flex-wrap gap-2">
              {tagFilters.map((tag) => {
                const isSelected = selectedTag === tag.slug

                return (
                  <button
                    key={tag.slug}
                    type="button"
                    aria-pressed={isSelected}
                    onClick={() => setSelectedTag(isSelected ? null : tag.slug)}
                    className="rounded-full border border-border bg-canvas px-3 py-1 text-xs text-ink-soft transition-colors hover:border-accent/50 hover:text-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent aria-pressed:border-accent aria-pressed:bg-accent/10 aria-pressed:text-accent"
                  >
                    {tag.label} <span className="font-mono text-ink-soft/70">{tag.count}</span>
                  </button>
                )
              })}
            </div>
          </div>
        )}

        <div className="flex flex-col gap-2 text-sm text-ink-soft sm:flex-row sm:items-center sm:justify-between">
          <p aria-live="polite">
            {results.length} {results.length === 1 ? 'post' : 'posts'} found
            {selectedTagLabel ? ` in ${selectedTagLabel}` : ''}
          </p>
          {hasFilters && (
            <button
              type="button"
              onClick={() => {
                setQuery('')
                setSelectedTag(null)
              }}
              className="self-start rounded-lg border border-border px-3 py-1.5 text-sm text-ink-soft transition-colors hover:border-accent/50 hover:text-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent sm:self-auto"
            >
              Clear filters
            </button>
          )}
        </div>
      </div>

      {results.length > 0 ? (
        <div className="space-y-12">
          {results.map((post, index) => (
            <PostCard key={post.slug} {...post} index={index} />
          ))}
        </div>
      ) : (
        <p className="text-ink-soft">No posts match your search.</p>
      )}
    </section>
  )
}
