import type {
  Badge,
  CLEARCHAT_message,
  Message,
  PRIVMSG_message,
  RawMessage,
  USERNOTICE_message
} from './types'

export function parseRawMessage(rawMessage: string, allBadges: Badge[]): Message {
  const rm: RawMessage = JSON.parse(rawMessage)
  const safeTag = (key: string) => rm.tags[key] ?? ''

  switch (rm.type) {
    case 'PRIVMSG': {
      const message: PRIVMSG_message = {
        username: rm.username,
        type: rm.type,
        channel: rm.channel,
        message: rm.message,
        tags: {
          '@badge-info': safeTag('@badge-info'),
          badges: parseRawBadges(safeTag('badges'), allBadges),
          'client-nonce': safeTag('client-nonce'),
          color: safeTag('color'),
          'display-name': safeTag('display-name'),
          emotes: safeTag('emotes'),
          'first-msg': Boolean(safeTag('first-msg')),
          flags: safeTag('flags'),
          id: safeTag('id'),
          mod: Boolean(safeTag('mod')),
          'returning-chatter': Boolean(safeTag('returning-chatter')),
          'room-id': safeTag('room-id'),
          subscriber: Boolean(safeTag('subscriber')),
          'tmi-sent-ts': Number(rm.tags['tmi-sent-ts']),
          turbo: Boolean(safeTag('turbo')),
          'user-id': safeTag('user-id'),
          'user-type': safeTag('user-type')
        }
      }
      return message
    }

    case 'CLEARCHAT': {
      const message: CLEARCHAT_message = {
        username: rm.username,
        type: rm.type,
        channel: rm.channel,
        message: rm.message,
        tags: {
          '@ban-duration': Number(safeTag('@band-duration')),
          'room-id': safeTag('@room-id'),
          'target-user-id': safeTag('target-user-id'),
          'tmi-sent-ts': Number(safeTag('tmi-sent-ts'))
        }
      }

      return message
    }

    case 'USERNOTICE': {
      const message: USERNOTICE_message = {
        username: rm.username,
        type: rm.type,
        channel: rm.channel,
        message: rm.message,
        tags: {
          '@badge-info': safeTag('@badge-info'),
          badges: parseRawBadges(safeTag('badges'), allBadges),
          color: safeTag('color'),
          'display-name': safeTag('displamessage.tags.badgey-name'),
          emotes: safeTag('emotes'),
          flags: safeTag('flags'),
          id: safeTag('id'),
          login: safeTag('login'),
          mod: Boolean(safeTag('mod')),
          'msg-id': safeTag('msg-id'),
          'msg-param-cumulative-months': Number(safeTag('msg-param-cumulative-months')),
          'msg-param-months': Number(safeTag('msg-param-months')),
          'msg-param-multimonth-duration': Number(safeTag('msg-param-multimonth-duration')),
          'msg-param-multimonth-tenure': Number(safeTag('msg-param-multimonth-tenure')),
          'msg-param-should-share-streak': Number(safeTag('msg-param-should-share-streak')),
          'msg-param-streak-months': Number(safeTag('msg-param-streak-months')),
          'msg-param-sub-plan': safeTag('msg-param-sub-plan'),
          'msg-param-sub-plan-name': safeTag('msg-param-sub-plan-name'),
          'msg-param-was-gifted': Boolean(safeTag('msg-param-was-gifted')),
          'room-id': safeTag('room-id'),
          subscriber: Boolean(safeTag('subscriber')),
          'system-msg': safeTag('system-msg'),
          'tmi-sent-ts': Number(safeTag('tmi-sent-ts')),
          'user-id': safeTag('user-id'),
          'user-type': safeTag('user-type'),
          vip: Boolean(safeTag('vip'))
        }
      }

      return message
    }

    default:
      throw new Error(`Unhandled message type: ${rm.type}`)
  }
}

export function parseRawBadges(rb: string, allBadges: Badge[]): Badge[] {
  const badges = rb.split(',')
  const formatedBadges = badges.map((badgeWithVersion: string) => {
    const [setId, version] = badgeWithVersion.split('/')
    return { setId, version }
  })

  const foundBadges: Badge[] = []
  if (!badges) return []

  for (const badgeToFind of formatedBadges) {
    const badge = allBadges
      .filter((badge) => badge.setId === badgeToFind.setId)
      .find((badge) => badge.version === badgeToFind.version)

    if (badge) {
      foundBadges.push(badge)
    }
  }

  return foundBadges
}
