import {
  FollowList,
  tryToFollowList,
  fromFollowListToHexSet,
  Pubkey,
  CannotFollowSelfError
} from '@/domain'
import client from '@/services/client.service'
import { createContext, useContext, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useNostr } from './NostrProvider'

type TFollowListContext = {
  followingSet: Set<string>
  followList: FollowList | null
  follow: (pubkey: string) => Promise<void>
  unfollow: (pubkey: string) => Promise<void>
}

const FollowListContext = createContext<TFollowListContext | undefined>(undefined)

export const useFollowList = () => {
  const context = useContext(FollowListContext)
  if (!context) {
    throw new Error('useFollowList must be used within a FollowListProvider')
  }
  return context
}

export function FollowListProvider({ children }: { children: React.ReactNode }) {
  const { t } = useTranslation()
  const { pubkey: accountPubkey, followListEvent, publish, updateFollowListEvent } = useNostr()

  // Create domain FollowList from event
  const followList = useMemo(
    () => tryToFollowList(followListEvent),
    [followListEvent]
  )

  // Legacy compatibility: expose as Set<string> for existing consumers
  const followingSet = useMemo(
    () => (followList ? fromFollowListToHexSet(followList) : new Set<string>()),
    [followList]
  )

  const follow = async (pubkey: string) => {
    if (!accountPubkey) return

    // Fetch latest follow list event
    const latestEvent = await client.fetchFollowListEvent(accountPubkey)
    if (!latestEvent) {
      const result = confirm(t('FollowListNotFoundConfirmation'))
      if (!result) return
    }

    // Create or update FollowList using domain object
    const ownerPubkey = Pubkey.fromHex(accountPubkey)
    const currentFollowList = latestEvent
      ? FollowList.fromEvent(latestEvent)
      : FollowList.empty(ownerPubkey)

    // Use domain logic for following
    const targetPubkey = Pubkey.tryFromString(pubkey)
    if (!targetPubkey) return

    try {
      const change = currentFollowList.follow(targetPubkey)
      if (change.type === 'no_change') return

      // Publish the updated follow list
      const draftEvent = currentFollowList.toDraftEvent()
      const newFollowListEvent = await publish(draftEvent)
      await updateFollowListEvent(newFollowListEvent)
    } catch (error) {
      if (error instanceof CannotFollowSelfError) {
        // Silently ignore self-follow attempts
        return
      }
      throw error
    }
  }

  const unfollow = async (pubkey: string) => {
    if (!accountPubkey) return

    const latestEvent = await client.fetchFollowListEvent(accountPubkey)
    if (!latestEvent) return

    // Use domain object for unfollowing
    const currentFollowList = FollowList.fromEvent(latestEvent)
    const targetPubkey = Pubkey.tryFromString(pubkey)
    if (!targetPubkey) return

    const change = currentFollowList.unfollow(targetPubkey)
    if (change.type === 'no_change') return

    // Publish the updated follow list
    const draftEvent = currentFollowList.toDraftEvent()
    const newFollowListEvent = await publish(draftEvent)
    await updateFollowListEvent(newFollowListEvent)
  }

  return (
    <FollowListContext.Provider
      value={{
        followingSet,
        followList,
        follow,
        unfollow
      }}
    >
      {children}
    </FollowListContext.Provider>
  )
}
