import {load} from 'cheerio'

/** @typedef {import('cheerio').Element} Element */
/** @typedef {import('cheerio').Cheerio<Element>} CheerioNode */

/** @type {(_1: string) => () => CheerioNode} */
export const loadImpl = html => () => {
  const root = load(html).root()
  const htmlC = root.first().children().first()

  if (!htmlC.is('html')) {
    throw new Error('invariant condition: root node should contain HTML!')
  }

  if (htmlC.length !== 1) {
    throw new Error('invariant condition: HTML element should be only child!')
  }

  return htmlC
}

/** @type {(_: CheerioNode) => () => Array<CheerioNode>} */
export const toArrayImpl = n => () => Array(n.length) .fill(undefined) .map((_, ix) => n.slice(ix, ix + 1))

/** @type {(_: CheerioNode) => () => CheerioNode | null} */
export const toNullableImpl = n => () => n.length === 0 ? null : n.first()

/** @type {(_: CheerioNode) => () => CheerioNode} */
export const childrenImpl = n => () => n.children()

/** @type {(_: CheerioNode) => () => CheerioNode} */
export const siblingsImpl = n => () => n.siblings()

/** @type {(_: CheerioNode) => () => CheerioNode} */
export const parentImpl = n => () => n.parent()

/** @type { (_2: CheerioNode) => () => Record<string, string>} */
export const attrsImpl = n => () => n.attr() || {}

/** @type {(_1: string) => (_2: CheerioNode) => () => string | null} */
export const attrImpl = k => n => () => n.attr(k) || null

/** @type {(_2: CheerioNode) => () => Record<string, string>} */
export const cssImpl = n => () => n.css() || {}

/** @type {(_2: CheerioNode) => () => string} */
export const htmlImpl = n => () => n.html() || ''

/** @type {(_2: CheerioNode) => () => string} */
export const textImpl = n => () => n.text() || ''

/** @type {(_1: string) => (_2: CheerioNode) => () => CheerioNode} */
export const findImpl = s => n => () => n.find(s)