import { MessageEntity, User } from 'typegram'

export interface FmtString {
  text: string
  entities?: MessageEntity[]
  parse_mode?: undefined
}

export class FmtString implements FmtString {
  constructor(public text: string, entities?: MessageEntity[]) {
    if (entities) {
      this.entities = entities
      // force parse_mode to undefined if entities are present
      this.parse_mode = undefined
    }
  }
  static normalise(content: string | FmtString) {
    if (typeof content === 'string') return new FmtString(content)
    return content
  }
}

export namespace Types {
  // prettier-ignore
  export type Containers = 'bold' | 'italic' | 'spoiler' | 'strikethrough' | 'underline'
  export type NonContainers = 'code' | 'pre'
  export type Text = Containers | NonContainers
}

type TemplateParts = string | TemplateStringsArray | string[]

export function _fmt(
  kind: Types.Containers | 'very-plain'
): (parts: TemplateParts, ...items: (string | FmtString)[]) => FmtString
export function _fmt(
  kind: Types.NonContainers
): (parts: TemplateParts, ...items: string[]) => FmtString
export function _fmt(
  kind: 'pre',
  opts: { language: string }
): (parts: TemplateParts, ...items: string[]) => FmtString
export function _fmt(kind: Types.Text | 'very-plain', opts?: object) {
  return function fmt(parts: TemplateParts, ...items: (string | FmtString)[]) {
    let text = ''
    const entities: MessageEntity[] = []
    parts = typeof parts === 'string' ? [parts] : parts
    for (let i = 0; i < parts.length; i++) {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      text += parts[i]!
      const item = items[i]
      if (!item) continue
      if (typeof item === 'string') {
        text += item
        continue
      }
      for (const child of item.entities || [])
        entities.push({ ...child, offset: text.length + child.offset })
      text += item.text
    }
    if (kind !== 'very-plain')
      entities.unshift({ type: kind, offset: 0, length: text.length, ...opts })
    return new FmtString(text, entities.length ? entities : undefined)
  }
}
export const linkOrMention = (
  content: string | FmtString,
  data:
    | { type: 'text_link'; url: string }
    | { type: 'text_mention'; user: User }
) => {
  const { text, entities = [] } = FmtString.normalise(content)
  entities.unshift(Object.assign(data, { offset: 0, length: text.length }))
  return new FmtString(text, entities)
}
