import { render, CustomElement, Host, ShadowDOM, defineElement, state, prop } from "@cerxes/csx";
import { testContainer } from "../utils/test-container";
import { nextAnimationFrame } from "../utils/next-animation-frame";

describe("Shadow-DOM tests", () => {
    /**
     * Assert that shadow dom behaves as expected
     */
    test("Simple shadow-component", async () => {
        @defineElement('shadow-component')
        class ShadowComponent extends CustomElement{
            @prop()
            title = 'Content here';
            
            render(){
                return (
                    <Host>
                        <ShadowDOM>
                            <div>
                                <h1>{this.title}</h1>
                                <slot />
                            </div>
                        </ShadowDOM>
                    </Host>
                )
            }
        }
        
        let initialVSpec = <ShadowComponent />;
        let rendered = render(initialVSpec);
        let container = testContainer(rendered);
        document.body.appendChild(container);// Components need to be added to the DOM or their connectecCallback will not be called
        
        // Initial render
        expect(
            container.innerHTML
        ).toBe([
            `<shadow-component>`,
            `</shadow-component>`,
        ].join(''));
    
        expect(
            rendered.shadowRoot.innerHTML
        ).toBe([
            `<div>`,
            `<h1>Content here</h1>`,
            `<slot></slot>`,
            `</div>`,
        ].join(''));
    
        // Update behaves as it should
        let updatedVSpec = (
            <ShadowComponent title={"New content here"}>
                <li><ul>contents</ul></li>
            </ShadowComponent>
        );
        render(updatedVSpec, {host: rendered, old: initialVSpec});
        
        // Wait for it to update
        await nextAnimationFrame();
    
        expect(
            container.innerHTML
        ).toBe([
            `<shadow-component>`,
            `<li><ul>contents</ul></li>`,
            `</shadow-component>`,
        ].join(''));
    
        expect(
            rendered.shadowRoot.innerHTML
        ).toBe([
            `<div>`,
            `<h1>New content here</h1>`,
            `<slot></slot>`,
            `</div>`,
        ].join(''));
    
        document.body.removeChild(container);
    });
    
    test("Nested shadow-component", async () => {
        @defineElement('todo-item')
        class TodoItem extends CustomElement {
            @prop()
            get model(){ return this.#model; }
            set model(value){ this.#model = value; }
            
            #model;
        
            render(){
                return (
                    <Host>
                        <ShadowDOM>
                            <input type="checkbox" checked={ this.model.checked }/>
                            <label>
                                <slot />
                            </label>
                        </ShadowDOM>
                    </Host>
                )
            }
        }
        
        @defineElement('my-todo')
        class MyTodo extends CustomElement {
            @state()
            todos = [
                { text: "todo 1", checked: true },
                { text: "todo 2", checked: false },
            ];
            
            rendered = [];
            
            render(){
                return (
                    <Host>
                        <h1>Todos</h1>
                        <ul>
                            {this.todos.map((todo,index)=>(
                                <TodoItem model={todo}
                                          ref={(el)=>this.rendered[index]=el}
                                >
                                    {todo.text}
                                </TodoItem>
                            ))}
                        </ul>
                    </Host>
                )
            }
        }
    
        let initialVSpec = <MyTodo />;
        let rendered = render(initialVSpec);
        let container = testContainer(rendered);
        document.body.appendChild(container);// Components need to be added to the DOM or their connectecCallback will not be called
        
        // Initial render
        expect(
            container.innerHTML
        ).toBe([
            `<my-todo>`,
            `<h1>Todos</h1>`,
            `<ul>`,
            ...rendered.todos.map(todo=>(
                `<todo-item>${todo.text}</todo-item>`
            )),
            `</ul>`,
            `</my-todo>`,
        ].join(''));
    
        for(let i = 0; i < rendered.todos.length; ++i){
            let todo = rendered.todos[i];
            let el = rendered.rendered[i];
            expect(el).not.toBeUndefined();
            expect(
                el.shadowRoot.innerHTML
            ).toBe([
                `<input type="checkbox"${todo.checked? ' checked=""': ''}>`,
                `<label>`,
                `<slot></slot>`,
                `</label>`
            ].join(''));
        }
    
        document.body.removeChild(container);
    });
    
});