import { FileServerMap } from '../types'
import { NostrController } from '../controllers'
import { SIGIT_BLOSSOM } from './const.ts'
import { unixNow } from './nostr.ts'
import { Filter, UnsignedEvent, kinds } from 'nostr-tools'
import NDK, {
  NDKEvent,
  NDKFilter,
  NDKSubscriptionOptions
} from '@nostr-dev-kit/ndk'

/**
 * Fetches the relays to get preferred file servers for the given npub
 * @param npub hex pubkey
 * @param fetchEvent provided by Nostr dev kit
 */
const getFileServerMap = async (
  npub: string,
  fetchEvent: (
    filter: NDKFilter,
    opts?: NDKSubscriptionOptions | undefined
  ) => Promise<NDKEvent | null>
): Promise<{ map: FileServerMap; mapUpdated?: number }> => {
  try {
    // More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/96.md
    const eventFilter: Filter = {
      kinds: [kinds.FileServerPreference],
      authors: [npub]
    }

    const event = await fetchEvent(eventFilter)

    if (event) {
      // Handle found event 10096
      const fileServersMap: FileServerMap = {}

      const serverTags = event.tags.filter((tag) => tag[0] === 'server')

      serverTags.forEach((tag) => {
        const url = tag[1]
        const serverType = tag[2]

        // if 3rd element of server tag is undefined, server is WRITE and READ
        fileServersMap[url] = {
          write: serverType ? serverType === 'write' : true,
          read: serverType ? serverType === 'read' : true
        }
      })

      return Promise.resolve({
        map: fileServersMap,
        mapUpdated: event.created_at
      })
    } else {
      return Promise.resolve({ map: getDefaultFileServerMap() })
    }
  } catch (err) {
    return Promise.reject(err)
  }
}

/**
 * Publishes a preferred file servers list for the given npub
 * @param serverMap list of preferred servers
 * @param npub hex pubkey
 * @param ndk provided by Nostr dev kit
 * @param publish provided by Nostr dev kit
 */
const publishFileServer = async (
  serverMap: FileServerMap,
  npub: string,
  ndk: NDK,
  publish: (event: NDKEvent) => Promise<string[]>
): Promise<string> => {
  const timestamp = unixNow()
  const serverURLs = Object.keys(serverMap)

  // More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/65.md
  const tags: string[][] = serverURLs.map((serverURL) => {
    const serverTag = ['server', serverURL]

    return serverTag.filter((value) => value !== '')
  })

  const newRelayMapEvent: UnsignedEvent = {
    kind: kinds.FileServerPreference,
    tags,
    content: '',
    pubkey: npub,
    created_at: timestamp
  }

  const nostrController = NostrController.getInstance()
  const signedEvent = await nostrController.signEvent(newRelayMapEvent)

  const ndkEvent = new NDKEvent(ndk, signedEvent)
  const publishResult = await publish(ndkEvent)

  if (publishResult && publishResult.length) {
    return Promise.resolve(
      `Preferred file servers published on: ${publishResult.join('\n')}`
    )
  }

  return Promise.reject(
    'Publishing updated preferred file servers was unsuccessful.'
  )
}

const getDefaultFileServerMap = (): FileServerMap => ({
  [SIGIT_BLOSSOM]: { write: true, read: true }
})

export { getFileServerMap, publishFileServer }
