import { NostrEvent } from '@nostr/tools/pure'
import { decodeNostrURI } from '@nostr/tools/nip19'
import { normalizeURL } from '@nostr/tools/utils'

import { getInboxRelaysFor, getOutboxRelaysFor, getWindowNostr, pool } from './nostr.js'
import { debounce } from './utils.js'

export default class NostrRSVP extends HTMLElement {
  static observedAttributes = ['ref']

  root: HTMLDivElement
  eventData: NostrEvent | null = null

  constructor() {
    super()

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

    this.root = document.createElement('div')
    shadowRoot!.appendChild(this.root)
  }

  connectedCallback() {
    this.style.display = 'block'
    this.style.width = 'fit-content'
    this.style.height = 'fit-content'
    this.set()
  }

  attributeChangedCallback() {
    this.set()
  }

  set: () => void = debounce(async () => {
    let input = this.getAttribute('ref')
    if (!input) return

    let { type, data } = decodeNostrURI(input)
    let relays: string[] = []
    let author: string | undefined
    let filter: any

    if (type === 'nevent') {
      let d = data as any
      relays = d.relays || []
      author = d.author
      filter = { ids: [d.id] }
    } else if (type === 'naddr') {
      let d = data as any
      relays = d.relays || []
      author = d.pubkey
      filter = {
        authors: [d.pubkey],
        kinds: [d.kind],
        '#d': [d.identifier],
      }
    } else if (input.match(/[0-9a-f]{64}/)) {
      filter = { ids: [input] }
    } else {
      return
    }

    relays = relays.map(normalizeURL)

    if (author) {
      let authorRelays = await getOutboxRelaysFor(author)
      relays.push(...authorRelays)
    }

    let evt = await pool.get(relays, filter)
    if (!evt || (evt.kind !== 31922 && evt.kind !== 31923)) {
      this.root.innerHTML = '<p part="error">Not a valid calendar event.</p>'
      return
    }

    this.eventData = evt

    // get title from tags
    const title = evt.tags.find((t: string[]) => t[0] === 'title')?.[1] || 'Untitled Event'

    // get start/end times or dates
    let timeStr = ''
    if (evt.kind === 31922) {
      // date-based event
      const start = evt.tags.find((t: string[]) => t[0] === 'start')?.[1]
      const end = evt.tags.find((t: string[]) => t[0] === 'end')?.[1] || start
      timeStr = `${start}${end !== start ? ` to ${end}` : ''}`
    } else {
      // time-based event
      const start = evt.tags.find((t: string[]) => t[0] === 'start')?.[1]
      const end = evt.tags.find((t: string[]) => t[0] === 'end')?.[1]
      if (start) {
        const startDate = new Date(parseInt(start) * 1000)
        timeStr = startDate.toLocaleString()
        if (end) {
          const endDate = new Date(parseInt(end) * 1000)
          timeStr += ` to ${endDate.toLocaleString()}`
        }
      }
    }

    const identifier = evt.tags.find((t: string[]) => t[0] === 'd')?.[1] || ''

    this.root.innerHTML = `
      <div part="container">
        <p part="title" title="${identifier}">${title}</p>
        <p part="time">${timeStr}</p>
        <p part="description">${evt.content}</p>
        <div part="buttons">
          <button part="button accepted"><slot name="accepted">Going</slot></button>
          <button part="button tentative"><slot name="tentative">Maybe</slot></button>
          <button part="button declined"><slot name="declined">Not going</slot></button>
        </div>
      </div>
    `

    // add click handlers for RSVP buttons
    const buttons = this.root.querySelectorAll('button')
    buttons[0].addEventListener('click', () => this.sendRSVP('accepted'))
    buttons[1].addEventListener('click', () => this.sendRSVP('tentative'))
    buttons[2].addEventListener('click', () => this.sendRSVP('declined'))
  }, 200)

  async sendRSVP(status: 'accepted' | 'tentative' | 'declined') {
    if (!this.eventData) {
      console.error('Cannot RSVP: missing event data')
      return
    }

    try {
      const signedEvent = await (
        await getWindowNostr()
      ).signEvent({
        kind: 31925,
        created_at: Math.floor(Date.now() / 1000),
        content: '',
        tags: [
          ['e', this.eventData.id],
          [
            'a',
            `${this.eventData.kind}:${this.eventData.pubkey}:${this.eventData.tags.find((t: string[]) => t[0] === 'd')?.[1]}`,
          ],
          ['d', Math.random().toString().substring(8)], // this is retarded
          ['status', status],
          ['p', this.eventData.pubkey],
        ],
      })
      const pubs = pool.publish(await getInboxRelaysFor(this.eventData!.pubkey), signedEvent)
      await Promise.all(pubs)

      // update UI to show response was sent
      const buttonsDiv = this.root.querySelector('[part="buttons"]')
      if (buttonsDiv) {
        buttonsDiv.innerHTML = `<p part="rsvp-sent"><slot name="rsvp-sent">RSVP sent: ${status}</slot></p>`
      }
    } catch (error) {
      console.error('failed to send RSVP:', error)
      const buttonsDiv = this.root.querySelector('[part="buttons"]')
      if (buttonsDiv) {
        buttonsDiv.innerHTML = `<p part="rsvp-failed"><slot name="rsvp-failed">failed to send RSVP: ${error}</slot></p>`
      }
    }
  }
}

window.customElements.define('nostr-rsvp', NostrRSVP)
