import { ExtendedKind } from '@/constants'
import { useGlobalRelayBootstrapDefaults } from '@/hooks/use-global-relay-bootstrap-defaults'
import { buildProfilePageReadRelayUrls } from '@/lib/favorites-feed-relays'
import { getReplaceableCoordinate } from '@/lib/event'
import {
  isNip58ProfileBadgesListEvent,
  LEGACY_PROFILE_BADGES_D_TAG,
  parseAddressableCoordinate,
  parseProfileBadgeEntries,
  resolveBadgeDisplayFromDefinition,
  type ResolvedProfileBadge
} from '@/lib/nip58-profile-badges'
import { isDirectProfileWallComment } from '@/lib/profile-wall-comments'
import { normalizeHexPubkey } from '@/lib/pubkey'
import { normalizeAnyRelayUrl } from '@/lib/url'
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
import { useDeletedEvent } from '@/providers/DeletedEventProvider'
import client, { replaceableEventService } from '@/services/client.service'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Event, kinds, type Filter } from 'nostr-tools'

const CACHE_DURATION = 5 * 60 * 1000
const wallCacheByKey = new Map<string, { badges: ResolvedProfileBadge[]; comments: Event[]; lastUpdated: number }>()

function relayListsContentKey(favoriteRelays: string[], blockedRelays: string[]): string {
  const fav = [...favoriteRelays].map((u) => normalizeAnyRelayUrl(u) || u).filter(Boolean).sort().join('\u0001')
  const blk = [...blockedRelays].map((u) => normalizeAnyRelayUrl(u) || u).filter(Boolean).sort().join('\u0001')
  return `${fav}\u0000${blk}`
}

export function useProfileWall(pubkey: string, profileEventId: string | undefined) {
  const { favoriteRelays, blockedRelays } = useFavoriteRelays()
  const useGlobalRelayBootstrap = useGlobalRelayBootstrapDefaults()
  const { isEventDeleted } = useDeletedEvent()
  const isEventDeletedRef = useRef(isEventDeleted)
  isEventDeletedRef.current = isEventDeleted

  const cacheKey = useMemo(() => `${pubkey}-profile-wall-v1`, [pubkey])
  const cached = wallCacheByKey.get(cacheKey)

  const [badges, setBadges] = useState<ResolvedProfileBadge[]>(cached?.badges ?? [])
  const [comments, setComments] = useState<Event[]>(cached?.comments ?? [])
  const [isLoading, setIsLoading] = useState(!cached)
  const [refreshToken, setRefreshToken] = useState(0)

  const relayListsKey = useMemo(
    () => relayListsContentKey(favoriteRelays, blockedRelays),
    [favoriteRelays, blockedRelays]
  )
  const favoriteRelaysRef = useRef(favoriteRelays)
  const blockedRelaysRef = useRef(blockedRelays)
  favoriteRelaysRef.current = favoriteRelays
  blockedRelaysRef.current = blockedRelays
  const useGlobalRelayBootstrapRef = useRef(useGlobalRelayBootstrap)
  useGlobalRelayBootstrapRef.current = useGlobalRelayBootstrap

  useEffect(() => {
    let cancelled = false

    const run = async () => {
      const mem = wallCacheByKey.get(cacheKey)
      if (mem && Date.now() - mem.lastUpdated < CACHE_DURATION && refreshToken === 0) {
        setBadges(mem.badges)
        setComments(mem.comments)
        setIsLoading(false)
        return
      }

      setIsLoading(true)

      let pkNorm = pubkey
      try {
        pkNorm = normalizeHexPubkey(pubkey)
      } catch {
        /* use raw */
      }

      const emptyAuthor = { read: [] as string[], write: [] as string[], httpRead: [] as string[], httpWrite: [] as string[] }
      const authorRl = await client.fetchRelayList(pubkey).catch(() => emptyAuthor)
      if (cancelled) return

      const relayUrls = buildProfilePageReadRelayUrls(
        favoriteRelaysRef.current,
        blockedRelaysRef.current,
        authorRl,
        false,
        false,
        [ExtendedKind.COMMENT, ExtendedKind.PROFILE_BADGES_LIST, ExtendedKind.BADGE_DEFINITION],
        useGlobalRelayBootstrapRef.current
      )

      // --- Badges (NIP-58) ---
      let listEvent =
        (await replaceableEventService.fetchReplaceableEvent(pkNorm, ExtendedKind.PROFILE_BADGES_LIST)) ??
        undefined
      if (!listEvent || !isNip58ProfileBadgesListEvent(listEvent)) {
        const legacy = await replaceableEventService.fetchReplaceableEvent(
          pkNorm,
          ExtendedKind.PROFILE_BADGES,
          LEGACY_PROFILE_BADGES_D_TAG
        )
        if (legacy && isNip58ProfileBadgesListEvent(legacy)) listEvent = legacy
      }

      const entries = parseProfileBadgeEntries(listEvent)
      const defCoords = [...new Set(entries.map((e) => e.definitionCoordinate))]
      const defByCoord = new Map<string, Event | undefined>()

      await Promise.all(
        defCoords.map(async (coord) => {
          const parsed = parseAddressableCoordinate(coord)
          if (!parsed || parsed.kind !== ExtendedKind.BADGE_DEFINITION) {
            defByCoord.set(coord, undefined)
            return
          }
          const defEvent = await replaceableEventService.fetchReplaceableEvent(
            parsed.pubkey,
            parsed.kind,
            parsed.d
          )
          defByCoord.set(coord, defEvent)
        })
      )

      const resolvedBadges = entries.map((entry) =>
        resolveBadgeDisplayFromDefinition(entry, defByCoord.get(entry.definitionCoordinate))
      )

      // --- Wall comments (kind 1111 on profile kind 0) ---
      let wallComments: Event[] = []
      const profileId = profileEventId?.trim().toLowerCase()
      if (profileId && /^[0-9a-f]{64}$/.test(profileId) && relayUrls.length > 0) {
        const profileCoord = getReplaceableCoordinate(kinds.Metadata, pkNorm, '')
        const filters: Filter[] = [
          { kinds: [ExtendedKind.COMMENT], '#e': [profileId], limit: 200 },
          { kinds: [ExtendedKind.COMMENT], '#a': [profileCoord], limit: 200 }
        ]
        const pool = new Map<string, Event>()
        try {
          const rows = await Promise.all(
            filters.map((filter) =>
              client.fetchEvents(relayUrls, filter, {
                cache: true,
                eoseTimeout: 4500,
                globalTimeout: 14_000
              })
            )
          )
          for (const batch of rows) {
            for (const e of batch) pool.set(e.id, e)
          }
        } catch {
          /* ignore */
        }

        wallComments = [...pool.values()]
          .filter(
            (e) =>
              !isEventDeletedRef.current(e) &&
              isDirectProfileWallComment(e, profileId, pkNorm)
          )
          .sort((a, b) => b.created_at - a.created_at)
      }

      if (cancelled) return
      setBadges(resolvedBadges)
      setComments(wallComments)
      wallCacheByKey.set(cacheKey, {
        badges: resolvedBadges,
        comments: wallComments,
        lastUpdated: Date.now()
      })
      setIsLoading(false)
    }

    void run()
    return () => {
      cancelled = true
    }
  }, [pubkey, profileEventId, cacheKey, refreshToken, relayListsKey])

  const refresh = useCallback(() => {
    wallCacheByKey.delete(cacheKey)
    setIsLoading(true)
    setRefreshToken((t) => t + 1)
  }, [cacheKey])

  return { badges, comments, isLoading, refresh }
}
