export default class ChatPanel extends HTMLElement
{
    static CLOSE_CLICK_EVT = 'close-click';
    static MESSAGE_SUBMIT_EVT = 'msg-submit';

    static observedAttributes = ['close', 'with', 'disabled'];

    constructor()
    {
        super();

        this._shadowRoot = this.attachShadow({ mode: 'closed' });
    
        this._shadowRoot.innerHTML = `
            <link href="./assets/css/bootstrap.min.css" rel="stylesheet" />
            <link href="./assets/icons/css/fontawesome.min.css" rel="stylesheet" />
            <link href="./assets/icons/css/solid.min.css" rel="stylesheet" />

            <style>
                .chat-entry {
                    margin-right: 1.5em;
                    background-color: var(--bs-secondary-bg-subtle);
                }

                .chat-entry.own {
                    margin-right: 0;
                    margin-left: 1.5em;
                    background-color: var(--bs-primary-bg-subtle);
                }

                .chat-entry:not(:last-child),
                .chat-sys:not(:last-child) {
                    margin-bottom: .75em;
                }
            </style>

            <div class="d-none d-md-block">
                <div class="hstack gap-2 align-items-baseline">
                    <h2 class="display-6 m-0">Chat</h2>
                    <span id="withLabel" class="lh-1" hidden>with <strong id="withLabelTxt">name</strong></span>
                </div>
                <hr>
            </div>
            <div id="chatArea" class="flex-grow-1 overflow-y-auto" style="height: 0;">
                <template>
                    <div class="vstack rounded p-2 chat-entry">
                        <strong class="chat-user-name"></strong>
                        <div class="hstack gap-2 justify-content-between align-items-baseline">
                            <span class="chat-msg text-break"></span>
                            <small class="chat-time text-body-tertiary"></small>
                        </div>
                    </div>
                </template>
            </div>
            <hr>
            <form id="chatForm" class="hstack gap-3">
                <input id="messageInput" type="text" class="form-control form-control" placeholder="Message">
                <button id="sendButton" type="submit" class="btn btn-primary"><i class="fa-solid fa-paper-plane"></i></button>
                <button id="closeButton" type="button" class="btn-close ms-auto" aria-label="Close" hidden></button>
            </form>
        `;

        this.withLabel = this._shadowRoot.querySelector('#withLabel');
        this.closeButton = this._shadowRoot.querySelector('#closeButton');
        this.chatForm = this._shadowRoot.querySelector('#chatForm');
        this.messageInput = this._shadowRoot.querySelector('#messageInput');
        this.sendButton = this._shadowRoot.querySelector('#sendButton');
        this.chatArea = this._shadowRoot.querySelector('#chatArea');

        // Bind event listeners
        this.boundOnChatCloseClick = this.onChatCloseCLick.bind(this);
        this.boundOnChatFormSubmit = this.onChatFormSubmit.bind(this);

        this.chatMsgCnt = 0;
    }

    connectedCallback()
    {
        // Add chat form submit listener
        this.chatForm.addEventListener('submit', this.boundOnChatFormSubmit);
    }

    attributeChangedCallback(name, oldValue, newValue)
    {
        switch (name)
        {
            case 'close':
                this.enableCloseButton(newValue === 'true');
                break;
            case 'with':
                this.showWithName(newValue);
                break;
            case 'disabled':
                this.disableInput(newValue === 'true');
                break;
        }
    }

    reset()
    {
        this.chatArea.querySelectorAll(':not(template)').forEach(child => child.remove());
        this.messageInput.value = '';
        this.lastSenderName = null;
        this.chatMsgCnt = 0;

        // Enable input by removing the "disabled" attribute
        this.removeAttribute('disabled');
    }

    enableCloseButton(enable)
    {
        // Show/hide close button
        this.closeButton.hidden = !enable;

        // Add/remove click listener
        if (enable)
            this.closeButton.addEventListener('click', this.boundOnChatCloseClick);
        else
            this.closeButton.removeEventListener('click', this.boundOnChatCloseClick);
    }

    showWithName(name)
    {
        if (name != null && name != '')
        {
            this.withLabel.querySelector('#withLabelTxt').innerText = name;
            this.withLabel.hidden = false;
        }
        else
            this.withLabel.hidden = true;
    }

    disableInput(disable)
    {
        this.messageInput.disabled = disable;
        this.sendButton.disabled = disable;
    }

    setFocus()
    {
        this.messageInput.focus();
    }

    /**
     * Display a chat message.
     */
    printChatMessage(message, senderName, isMe, date = new Date())
    {
        this.chatMsgCnt++;

        // Create chat entry from template
        const template = this._shadowRoot.querySelectorAll('template')[0];
        
        let node = template.content.cloneNode(true);
        node.firstElementChild.dataset.cnt = this.chatMsgCnt;

        if (isMe)
            node.firstElementChild.classList.add('own');

        // Add chat entry to container
        this.chatArea.append(node);
        
        // When a node is cloned from a template, the cloned content is initially a DocumentFragment
        // When we append this fragment to the DOM, it gets consumed, meaning the original fragment is emptied after appending
        // So we need to re-select the element after appending
        node = this.chatArea.querySelector(`[data-cnt="${this.chatMsgCnt}"`);

        // Print sender name, unless they are the same of the last message
        const nameTag = node.querySelector('.chat-user-name');
        if (senderName == this.lastSenderName)
            node.removeChild(nameTag);
        else
        {
            nameTag.innerText = senderName;
            this.lastSenderName = senderName;
        }

        // Print chat message
        const messageTag = node.querySelector('.chat-msg');
        messageTag.innerText = message;

        // Print time, formatted with leading zeros
        const currentHour = date.getHours();
        const currentMinute = date.getMinutes();

        const timeTag = node.querySelector('.chat-time');
        timeTag.innerText = `${currentHour.toString().padStart(2, '0')}:${currentMinute.toString().padStart(2, '0')}`;

        // Scroll view to bottom
        this.scrollChatToBottom();
    }

    /**
     * Display a system message.
     */
    printSystemMessage(message)
    {
        // Print message
        this.chatArea.innerHTML += `<div class="chat-sys text-secondary text-center"><small><em>${message}</em></small></div>\n`;

        // Scroll view to bottom
        this.scrollChatToBottom();
    }

    /**
     * Scroll the chat to bottom.
     */
    scrollChatToBottom()
    {
        this.chatArea.scrollTop = this.chatArea.scrollHeight;
    }

    onChatCloseCLick(e)
    {
        // Fire custom event
        this.dispatchEvent(
            new CustomEvent(ChatPanel.CLOSE_CLICK_EVT)
        );
    }

    onChatFormSubmit(e)
    {
        e.preventDefault();

        if (this.messageInput.value != '')
        {
            // Fire custom event
            this.dispatchEvent(
                new CustomEvent(ChatPanel.MESSAGE_SUBMIT_EVT, {
                    detail: {
                        message: this.messageInput.value
                    }
                })
            );

            // Reset message input
            this.messageInput.value = '';
            this.messageInput.focus();
        }
    }
}

customElements.define('chat-panel', ChatPanel);