import { Filter, SimplePool } from 'nostr-tools'
import { RelayList } from 'nostr-tools/kinds'
import { Event } from 'nostr-tools'
import { localCache } from '../services'
import { ONE_WEEK_IN_MS, SIGIT_RELAY } from './const.ts'
import { RelayMap, RelaySet } from '../types'

const READ_MARKER = "read"
const WRITE_MARKET = "write"

/**
 * Attempts to find a relay list from the provided lookUpRelays.
 * If the relay list is found, it will be added to the user relay list metadata.
 * @param lookUpRelays
 * @param hexKey
 * @return found relay list or null
 */
const findRelayListAndUpdateCache = async (lookUpRelays: string[], hexKey: string): Promise<Event | null> => {
  try {
    const eventFilter: Filter = {
      kinds: [RelayList],
      authors: [hexKey]
    }
    const pool = new SimplePool()
    const event = await pool.get(lookUpRelays, eventFilter)
    if (event) {
      await localCache.addUserRelayListMetadata(event)
    }
    return event
  } catch (error) {
    console.error(error)
    return null
  }
}

/**
 * Attempts to find a relay list in cache. If it is present, it will check that the cached event is not
 * older than one week.
 * @param hexKey
 * @return RelayList event if it's not older than a week; otherwise null
 */
const findRelayListInCache = async (hexKey: string): Promise<Event | null> => {
  try {
    // Attempt to retrieve the metadata event from the local cache
    const cachedRelayListMetadataEvent = await localCache.getUserRelayListMetadata(hexKey)

    // Check if the cached event is not older than one week
    if (cachedRelayListMetadataEvent && isOlderThanOneWeek(cachedRelayListMetadataEvent.cachedAt)) {
      return cachedRelayListMetadataEvent.event
    }

    return null
  } catch (error) {
    console.error(error)
    return null
  }
}

/**
 * Transforms a list of relay tags from a Nostr Event to a RelaySet.
 * @param tags
 */
const getUserRelaySet = (tags: string[][]): RelaySet => {
  return tags
    .filter(isRelayTag)
    .reduce<RelaySet>(toRelaySet, getDefaultRelaySet())
}

const getDefaultRelaySet = (): RelaySet => ({
  read: [SIGIT_RELAY],
  write: [SIGIT_RELAY]
})

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

const isOlderThanOneWeek = (cachedAt: number) => {
  return Date.now() - cachedAt < ONE_WEEK_IN_MS
}

const isRelayTag = (tag: string[]): boolean => tag[0] === 'r'

const toRelaySet = (obj: RelaySet, tag: string[]): RelaySet => {
  if (tag.length >= 3) {
    const marker = tag[2]

    if (marker === READ_MARKER) {
      obj.read.push(tag[1])
    } else if (marker === WRITE_MARKET) {
      obj.write.push(tag[1])
    }
  }
  if (tag.length === 2) {
    obj.read.push(tag[1])
    obj.write.push(tag[1])
  }

  return obj
}

export {
  findRelayListAndUpdateCache,
  findRelayListInCache,
  getUserRelaySet,
  getDefaultRelaySet,
  getDefaultRelayMap,
}