import { ApproxStructure, Assertions, Chain, Step, UiFinder } from '@ephox/agar';
import { UnitTest } from '@ephox/bedrock-client';
import { Arr, Optional } from '@ephox/katamari';
import * as AddEventsBehaviour from 'ephox/alloy/api/behaviour/AddEventsBehaviour';
import * as Behaviour from 'ephox/alloy/api/behaviour/Behaviour';
import { Reflecting } from 'ephox/alloy/api/behaviour/Reflecting';
import * as GuiFactory from 'ephox/alloy/api/component/GuiFactory';
import * as AlloyEvents from 'ephox/alloy/api/events/AlloyEvents';
import * as GuiSetup from 'ephox/alloy/api/testhelpers/GuiSetup';
import { Container } from 'ephox/alloy/api/ui/Container';
UnitTest.asynctest('ReflectingTest', (success, failure) => {
    GuiSetup.setup((store, _doc, _body) => {
        const makeChild = (label) => ({
            dom: {
                tag: 'span',
                innerHtml: label,
                styles: {
                    display: 'inline-block',
                    border: '1px solid #ccc',
                    margin: '0.5em',
                    padding: '1em'
                }
            },
            behaviours: Behaviour.derive([
                AddEventsBehaviour.config('child-events', [
                    AlloyEvents.runOnAttached(store.adder('child.' + label + '.attached')),
                    AlloyEvents.runOnDetached(store.adder('child.' + label + '.detached'))
                ])
            ])
        });
        return GuiFactory.build(Container.sketch({
            components: [
                {
                    dom: {
                        tag: 'div',
                        classes: ['reflector-1']
                    },
                    components: [
                        makeChild('state-changes-only')
                    ],
                    behaviours: Behaviour.derive([
                        Reflecting.config({
                            channel: 'channel-1',
                            updateState: (_, input) => Optional.some({ state: input })
                        })
                    ])
                },
                {
                    dom: {
                        tag: 'div',
                        classes: ['reflector-2a']
                    },
                    components: [],
                    behaviours: Behaviour.derive([
                        Reflecting.config({
                            channel: 'channel-2',
                            renderComponents: (input) => Arr.map(input, makeChild),
                            initialData: ['2a-cat', '2a-dog']
                        })
                    ])
                },
                {
                    dom: {
                        tag: 'div',
                        classes: ['reflector-2b']
                    },
                    components: [
                        makeChild('render-only-inital-component')
                    ],
                    behaviours: Behaviour.derive([
                        Reflecting.config({
                            channel: 'channel-2',
                            renderComponents: (input) => Arr.map(input, makeChild),
                            initialData: ['2b-cat', '2b-dog']
                        })
                    ])
                },
                {
                    dom: {
                        tag: 'div',
                        classes: ['reflector-2c']
                    },
                    components: [],
                    behaviours: Behaviour.derive([
                        Reflecting.config({
                            channel: 'channel-2',
                            renderComponents: (input) => Arr.map(input, makeChild)
                        })
                    ])
                },
                {
                    dom: {
                        tag: 'div',
                        classes: ['reflector-3']
                    },
                    components: [],
                    behaviours: Behaviour.derive([
                        Reflecting.config({
                            channel: 'channel-3',
                            renderComponents: (_input, state) => Arr.map(state.map((s) => s.state).getOr([]), makeChild),
                            updateState: (_c, input) => Optional.some({ state: input })
                        })
                    ])
                }
            ]
        }));
    }, (_doc, _body, gui, component, store) => {
        const sAssertReflectState = (label, expected, selector) => Chain.asStep(component.element, [
            UiFinder.cFindIn(selector),
            Chain.binder(component.getSystem().getByDom),
            Chain.op((r1) => {
                const actual = Reflecting.getState(r1).get().getOrDie();
                Assertions.assertEq('Checking state for: ' + label, expected, actual.state);
            })
        ]);
        return [
            store.sAssertEq('Checking the original sequence of attached and detached', [
                'child.state-changes-only.attached',
                'child.2a-cat.attached',
                'child.2a-dog.attached',
                // This one is removed immediately due to initialData being set
                'child.render-only-inital-component.attached',
                'child.render-only-inital-component.detached',
                'child.2b-cat.attached',
                'child.2b-dog.attached'
            ]),
            store.sClear,
            Assertions.sAssertStructure('Checking initial structure', ApproxStructure.build((s, str, _arr) => s.element('div', {
                children: [
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('state-changes-only'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('2a-cat'))] }),
                            s.element('span', { children: [s.text(str.is('2a-dog'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('2b-cat'))] }),
                            s.element('span', { children: [s.text(str.is('2b-dog'))] })
                        ]
                    }),
                    s.element('div', {
                        children: []
                    }),
                    s.element('div', {
                        children: []
                    })
                ]
            })), component.element),
            Step.sync(() => {
                gui.broadcastOn(['channel-1'], { blah: true });
            }),
            sAssertReflectState('reflector 1', { blah: true }, '.reflector-1'),
            store.sAssertEq('No attached/detached should have occurred', []),
            Step.sync(() => {
                gui.broadcastOn(['channel-2'], ['alpha', 'beta']);
            }),
            store.sAssertEq('Attached and detached should have occurred when broadcasting on channel 2', [
                'child.2a-cat.detached',
                'child.2a-dog.detached',
                'child.alpha.attached',
                'child.beta.attached',
                'child.2b-cat.detached',
                'child.2b-dog.detached',
                'child.alpha.attached',
                'child.beta.attached',
                'child.alpha.attached',
                'child.beta.attached'
            ]),
            Assertions.sAssertStructure('Checking structure after broadcast on channel-2', ApproxStructure.build((s, str, _arr) => s.element('div', {
                children: [
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('state-changes-only'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('alpha'))] }),
                            s.element('span', { children: [s.text(str.is('beta'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('alpha'))] }),
                            s.element('span', { children: [s.text(str.is('beta'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('alpha'))] }),
                            s.element('span', { children: [s.text(str.is('beta'))] })
                        ]
                    }),
                    s.element('div', {
                        children: []
                    })
                ]
            })), component.element),
            Step.sync(() => {
                gui.broadcastOn(['channel-3'], ['gamma']);
            }),
            Assertions.sAssertStructure('Checking structure after broadcast on channel-3', ApproxStructure.build((s, str, _arr) => s.element('div', {
                children: [
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('state-changes-only'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('alpha'))] }),
                            s.element('span', { children: [s.text(str.is('beta'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('alpha'))] }),
                            s.element('span', { children: [s.text(str.is('beta'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('alpha'))] }),
                            s.element('span', { children: [s.text(str.is('beta'))] })
                        ]
                    }),
                    s.element('div', {
                        children: [
                            s.element('span', { children: [s.text(str.is('gamma'))] })
                        ]
                    })
                ]
            })), component.element),
            sAssertReflectState('reflector3', ['gamma'], '.reflector-3')
        ];
    }, () => success(), failure);
});
//# sourceMappingURL=ReflectingTest.js.map