// This is an invisible template tag for enabling syntax highlighting
// of any string in most editors.
export function html(strings) {
    return strings.raw[0]
}

export let test = function (name, template, callback) {
    it(name, () => {
        injectHtmlAndBootAlpine(cy, template, callback)
    })
}

test.only = (name, template, callback) => {
    it.only(name, () => {
        injectHtmlAndBootAlpine(cy, template, callback)
    })
}

test.retry = (count) => (name, template, callback) => {
    it(name, {
        retries: {
            // During "cypress run"
            runMode: count - 1,
            // During "cypress open"
            openMode: count - 1,
        }
    }, () => {
        injectHtmlAndBootAlpine(cy, template, callback)
    })
}

test.csp = (name, template, callback) => {
    it(name, () => {
        injectHtmlAndBootAlpine(cy, template, callback, __dirname+'/spec-csp.html')
    })
}

function injectHtmlAndBootAlpine(cy, templateAndPotentiallyScripts, callback, page) {
    let [template, scripts] = Array.isArray(templateAndPotentiallyScripts)
        ? templateAndPotentiallyScripts
        : [templateAndPotentiallyScripts]

    cy.visit(page || __dirname+'/spec.html')

    cy.get('#root').then(([el]) => {
        el.innerHTML = template

        el.evalScripts(scripts)

        cy.get('[alpine-is-ready]', { timeout: 5000 }).should('be.visible');

        // We can't just simply reload a page from a test, because we need to
        // re-inject all the templates and such. This is a helper to allow
        // a test-subject method to perform a redirect all on their own.
        let reload = () => {
            cy.reload()

            cy.get('#root').then(([el]) => {
                el.innerHTML = template

                el.evalScripts(scripts)

                cy.get('[alpine-is-ready]', { timeout: 5000 }).should('be.visible');
            })
        }

        callback(cy, reload)
    })
}

export let haveData = (key, value) => ([ el ]) => expect(root(el)._x_dataStack[0][key]).to.equal(value)

export let haveFocus = () => el => expect(el).to.have.focus

export let notHaveFocus = () => el => expect(el).not.to.be.focused

export let haveAttribute = (name, value) => el => expect(el).to.have.attr(name, value)

export let notHaveAttribute = (name, value) => el => expect(el).not.to.have.attr(name, value)

export let haveText = text => el => expect(el).to.have.text(text)

export let notHaveText = text => el => expect(el).not.to.have.text(text)

export let beChecked = () => el => expect(el).to.be.checked

export let notBeChecked = () => el => expect(el).not.to.be.checked

export let beVisible = () => el => expect(el).to.be.visible

export let notBeVisible = () => el => expect(el).not.to.be.visible

export let beHidden = () => el => expect(el).to.be.hidden

export let haveClasses = classes => el => classes.forEach(aClass => expect(el).to.have.class(aClass))

export let notHaveClasses = classes => el => classes.forEach(aClass => expect(el).not.to.have.class(aClass))

export let haveValue = value => el => expect(el).to.have.value(value)

export let haveLength = length => el => expect(el).to.have.length(length)

export function root(el) {
    if (el._x_dataStack) return el

    if (! el.parentElement) return

    return closestRoot(el.parentElement)
}