import { getOutboxRelaysFor, getWindowNostr, inputToPubkey, pool } from './nostr.js'

export default class NostrFollow extends HTMLElement {
  static observedAttributes = ['pubkey']

  button: HTMLButtonElement
  slotDefault: HTMLSlotElement
  slotLoading: HTMLSlotElement
  slotSuccess: HTMLSlotElement
  slotFailure: HTMLSlotElement

  constructor() {
    super()

    this.attachShadow({ mode: 'open' })
    const { shadowRoot } = this

    this.button = document.createElement('button')
    this.button.setAttribute('part', 'button')
    this.button.onclick = this.handleClick.bind(this)

    this.slotDefault = document.createElement('slot')
    this.slotDefault.textContent = 'Follow me on Nostr!'
    this.button.appendChild(this.slotDefault)

    this.slotLoading = document.createElement('slot')
    this.slotLoading.name = 'loading'
    this.slotLoading.style.display = 'none'
    this.slotLoading.textContent = 'Following...'
    this.button.appendChild(this.slotLoading)

    this.slotSuccess = document.createElement('slot')
    this.slotSuccess.name = 'success'
    this.slotSuccess.style.display = 'none'
    this.slotSuccess.textContent = 'Followed.'
    this.button.appendChild(this.slotSuccess)

    this.slotFailure = document.createElement('slot')
    this.slotFailure.name = 'failure'
    this.slotFailure.style.display = 'none'
    this.slotFailure.textContent = 'Error: '
    this.button.appendChild(this.slotFailure)

    const errorMessage = document.createElement('span')
    errorMessage.setAttribute('part', 'error-message')
    this.slotFailure.appendChild(errorMessage)

    shadowRoot!.appendChild(this.button)
  }

  *queryPart<H = HTMLElement>(name: string): Iterable<H> {
    let slotted = this.button.querySelectorAll(`[part="${name}"]`)
    for (let i = 0; i < slotted.length; i++) {
      yield slotted[i] as H
    }
    let slotted2 = this.querySelectorAll(`[part="${name}"]`)
    for (let i = 0; i < slotted2.length; i++) {
      yield slotted2[i] as H
    }
  }

  async handleClick() {
    const [target, hints] = await inputToPubkey(this.getAttribute('pubkey')!)
    if (!target) return

    this.button.onclick = null
    this.button.disabled = true
    this.slotLoading.style.display = ''
    this.slotDefault.style.display = 'none'

    try {
      // get current follows
      const publicKey = await (await getWindowNostr()).getPublicKey()
      const relays = await getOutboxRelaysFor(publicKey)
      relays.push(...hints)

      // fetch existing contact list
      const res = await pool.querySync(relays, {
        kinds: [3],
        authors: [publicKey],
      })
      if (res.length === 0) {
        throw new Error("couldn't find your contact list")
      }

      const event = res[0]

      if (event.tags.find(tag => tag[1] === target)) {
        // already following
        this.slotLoading.style.display = 'none'
        this.slotSuccess.style.display = ''
        this.dispatchEvent(
          new CustomEvent('success', {
            bubbles: true,
            detail: { already: true },
          }),
        )
        return
      }

      event.tags.push(['p', target, relays[0]])
      event.created_at = Math.round(Date.now() / 1000)

      const signedEvent = await (await getWindowNostr()).signEvent(event)
      await Promise.any(pool.publish(relays, signedEvent))

      this.slotLoading.style.display = 'none'
      this.slotSuccess.style.display = ''
      this.dispatchEvent(
        new CustomEvent('success', {
          bubbles: true,
          detail: { already: false },
        }),
      )
    } catch (error) {
      console.warn('failed to follow:', error)

      this.slotLoading.style.display = 'none'
      this.slotFailure.style.display = ''
      this.dispatchEvent(
        new CustomEvent('failure', {
          bubbles: true,
          detail: { message: String(error) },
        }),
      )
      for (let el of this.queryPart('error-message')) {
        let msg = String(error)
        if (msg.startsWith('Error:')) {
          msg = msg.substring(6)
        }
        el.textContent = msg.trim()
      }
    }
  }
}

window.customElements.define('nostr-follow', NostrFollow)
