import {
    getDocument,
    getWindow,
    getDocumentFragmentFromString,
    waitForCustomElement,
} from "../../../source/dom/util.mjs";

import {getContainingDocument} from "../../../source/dom/util.mjs";

import {initJSDOM} from "../../util/jsdom.mjs";

import {expect} from "chai"


describe('DOM', function () {
    before(async function () {
        initJSDOM();
    })
    describe('getDocument()', function () {
        it('should return document object', function () {
            let d = getDocument();
            expect(typeof d).is.equal('object');
        });
    });

    describe('getWindow()', function () {
        it('should return document object', function () {
            let d = getWindow();
            expect(typeof d).is.equal('object');
        });
    });

    describe('getDocumentFragmentFromString()', function () {


        [
            ['<div></div>'],
            ['<div><div>2</div></div>'],
            ['<div><div>2</div>', '<div><div>2</div></div>'], // test invalid html
        ].forEach(function (data) {

            let a = data.shift()
            let b = data.shift()

            if (!b) b = a;

            it('should return documentFragment object ' + a, function () {
                let fragment = getDocumentFragmentFromString(a);
                expect(fragment).to.be.instanceOf(DocumentFragment);

                var div = document.createElement('div');
                div.appendChild(fragment.cloneNode(true));
                expect(div.innerHTML).to.be.equal(b);
            });

        });

    });


    describe('getContainingDocument', () => {
        let  jsDomDocument;

         beforeEach(() => {
             jsDomDocument = getDocument();
         });
        //
        // afterEach(() => {
        //     dom.window.close();
        // });

        it('should throw an error when called with an invalid argument', () => {
            expect(() => getContainingDocument(null)).to.throw('Invalid argument. Expected an HTMLElement.');
        });

        it('should return the correct containing document for an element in the main document', () => {
            const element = jsDomDocument.createElement('div');
            const containingDocument = getContainingDocument(element);

            expect(containingDocument).to.null;
        });

        it('should return the correct containing document for an element inside a shadow root', () => {
            const host = jsDomDocument.createElement('div');
            const shadowRoot = host.attachShadow({ mode: 'open' });
            const element = jsDomDocument.createElement('span');
            shadowRoot.appendChild(element);

            const containingDocument = getContainingDocument(element);
            expect(containingDocument).to.not.null;
        });

        it('should return the correct containing document for an element inside a nested shadow root', () => {
            const outerHost = jsDomDocument.createElement('div');
            const outerShadowRoot = outerHost.attachShadow({ mode: 'open' });

            const innerHost = jsDomDocument.createElement('div');
            outerShadowRoot.appendChild(innerHost);

            const innerShadowRoot = innerHost.attachShadow({ mode: 'open' });

            const element = jsDomDocument.createElement('span');
            innerShadowRoot.appendChild(element);

            const containingDocument = getContainingDocument(element);
            expect(containingDocument).to.not.null;
        });

        it('should return null when the element is not attached to any document', () => {
            const detachedElement = jsDomDocument.createElement('div');
            detachedElement.remove();

            const containingDocument = getContainingDocument(detachedElement);
            expect(containingDocument).to.be.null;
        });
    });    

    describe('waitForCustomElement()', () => {
        it('should resolve for a specific instance once defined', async () => {
            const tagName = `test-tabs-${Date.now()}`;
            const element = document.createElement(tagName);
            document.body.appendChild(element);

            const promise = waitForCustomElement(element, { method: 'ping' });

            class TestTabs extends HTMLElement {
                ping() {
                    return 'pong';
                }
            }

            customElements.define(tagName, TestTabs);

            const ready = await promise;
            expect(ready).to.equal(element);
            expect(ready.ping()).to.equal('pong');
        });

        it('should wait for a readyCheck condition', async () => {
            const tagName = `test-ready-${Date.now()}`;
            const element = document.createElement(tagName);
            document.body.appendChild(element);

            const promise = waitForCustomElement(element, {
                readyCheck: (el) => el.hasAttribute('data-ready'),
            });

            class TestReady extends HTMLElement {
                connectedCallback() {
                    setTimeout(() => {
                        this.setAttribute('data-ready', 'true');
                    }, 0);
                }
            }

            customElements.define(tagName, TestReady);

            const ready = await promise;
            expect(ready).to.equal(element);
            expect(ready.getAttribute('data-ready')).to.equal('true');
        });
    });
    
});    
