import NDK, { NDKEvent, NDKRelayList } from '@nostr-dev-kit/ndk'
import { kinds, UnsignedEvent } from 'nostr-tools'
import { normalizeWebSocketURL, unixNow } from '.'
import { NostrController } from '../controllers'
import { RelayMap } from '../types'
import { SIGIT_RELAY } from './const'

export const getRelayMapFromNDKRelayList = (ndkRelayList: NDKRelayList) => {
  const relayMap: RelayMap = {}

  ndkRelayList.readRelayUrls.forEach((relayUrl) => {
    const normalizedUrl = normalizeWebSocketURL(relayUrl)

    relayMap[normalizedUrl] = {
      read: true,
      write: false
    }
  })

  ndkRelayList.writeRelayUrls.forEach((relayUrl) => {
    const normalizedUrl = normalizeWebSocketURL(relayUrl)

    const existing = relayMap[normalizedUrl]
    if (existing) {
      existing.write = true
    } else {
      relayMap[normalizedUrl] = {
        read: false,
        write: true
      }
    }
  })

  return relayMap
}

export const getDefaultRelayMap = (): RelayMap => ({
  [SIGIT_RELAY]: { write: true, read: true }
})

/**
 * Publishes relay map.
 * @param relayMap - relay map.
 * @param npub - user's npub.
 * @param extraRelaysToPublish - optional relays to publish relay map.
 * @returns - promise that resolves into a string representing publishing result.
 */
export const publishRelayMap = async (
  relayMap: RelayMap,
  npub: string,
  ndk: NDK,
  publish: (event: NDKEvent) => Promise<string[]>
): Promise<string> => {
  const timestamp = unixNow()
  const relayURIs = Object.keys(relayMap)

  // More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/65.md
  const tags: string[][] = relayURIs.map((relayURI) =>
    [
      'r',
      relayURI,
      relayMap[relayURI].read && relayMap[relayURI].write
        ? ''
        : relayMap[relayURI].write
          ? 'write'
          : 'read'
    ].filter((value) => value !== '')
  )

  const newRelayMapEvent: UnsignedEvent = {
    kind: kinds.RelayList,
    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(
      `Relay Map published on: ${publishResult.join('\n')}`
    )
  }

  return Promise.reject('Publishing updated relay map was unsuccessful.')
}
