define(['jquery', 'underscore', '../../UI/ai/chat/loading-template-conversation.tmpl', '../../UI/ai/chat/conversation-template.tmpl'], function ($, _, loading_template_conversation, conversation_template) {
    var STATE_INITIAL      = 'initial';
    var STATE_RESET        = 'reset';
    var STATE_CONVERSATION = 'conversation';

    var LOADING_STATE_INITIAL      = 'initial';
    var LOADING_STATE_CONVERSATION = 'conversation';

    var CLASS_AI_ACTION_SHEET_OPENED = 'ai-action-sheet-open';

    var TRIGGER_EVENT_ON_SET_UP                         = 'onSetUp';
    var TRIGGER_EVENT_ON_INTRODUCTION_TEMPLATE          = 'onIntroductionTemplate';
    var TRIGGER_EVENT_ON_INTRODUCTION_TEMPLATE_RENDERED = 'onIntroductionTemplateRendered';

    var TEXT_PART_ROLE_MODEL = 'model';
    var TEXT_PART_ROLE_USER  = 'user';

        function TextPart(text, role) {
        this.text        = text;
        this.role        = role;
        this.part        = 'text';

        this.serialise = function () {
            return {
                text: this.text,
                role: this.role,
                part: this.part
            };
        };
    }

    function FilePart(mimeType, data) {
        this.mimeType = mimeType;
        this.data = data;
        this.role = 'user';

        this.serialise = function () {
            return {
                mime_type: this.mimeType,
                data: this.data,
                role: this.role
            };
        };
    }

    var AiChatConversation = function () {
        var $this = this;

        this.Initialise = function (config, plugins) {
            this.$modal = $('#ai-chat-conversation');

            this.$loading          = this.$modal.find('.ai-loading');
            this.$form             = this.$modal.find('.ai-form');
            this.$introduction     = $('<div></div>');
            this.$body             = this.$modal.find('.ai-body');
            this.$conversations    = this.$modal.find('.ai-conversations');
            this.$action_sheet     = $('.ai-action-sheet');
            this.$text_prompt_area = this.$modal.find('.js-textarea-prompt');
            this.$trigger_button   = $('.ai-chat-conversation-button');
            this.$footer           = this.$modal.find('.ai-footer');
            this.$xhr              = null;

            this.feedback_touched = false;
            this.history          = [];
            this.loading          = false;
            this.plugins          = [];
            this.language         = config.language;
            this.module           = config.module || '';
            this.metadata         = config.initial;

            if (plugins) {
                for (var i = 0; i < plugins.length; i++) {
                    var plugin = plugins[i];

                    plugin.install(this);

                    this.plugins.push(plugin);
                }
            }

            this.Listeners();
        };

        this.ShowLoading = function (loading, type) {
            type = type ? type : LOADING_STATE_CONVERSATION;

            if (loading) {
                switch (type) {
                    case LOADING_STATE_INITIAL: {
                        this.$loading.show();
                        this.$footer.hide();
                        this.$body.removeClass('overflow-auto');
                    }
                        break;
                    case LOADING_STATE_CONVERSATION: {
                        this.$conversations.find('.ai-conversations-content').append(loading_template_conversation());

                        this.ScrollToLatestMessage();
                    }
                        break;
                }

                this.$modal.find('.ai-introduction').remove();
                this.$form.find('button').attr('disabled', true);
            } else {
                this.$conversations.find('.ai-conversation-loading').remove();
                this.$loading.hide();
                this.$footer.show();
                this.$body.addClass('overflow-auto');

                this.$form.find('button').attr('disabled', false);
            }

            this.loading = !this.loading;
        }

        this.IsLoading = function () {
            return this.loading;
        };

        this.ShowIntroduction = function () {
            if (this.HasTrigger(TRIGGER_EVENT_ON_INTRODUCTION_TEMPLATE)) {
                var $introduction = this.TriggerEvent(TRIGGER_EVENT_ON_INTRODUCTION_TEMPLATE);
                $introduction.addClass('ai-introduction');

                // Remove any existing ai introduction
                this.$modal.find('.ai-introduction').remove();

                this.$modal.find('.ai-body').append($introduction);
                this.$introduction = this.$modal.find('.ai-introduction');

                this.TriggerEvent(TRIGGER_EVENT_ON_INTRODUCTION_TEMPLATE_RENDERED);
            }
        };

        this.ShowState = function (stateType) {
            switch (stateType) {
                case STATE_RESET: {
                    this.ResetValidation();
                    this.history = [];
                    this.$conversations.find('.ai-conversations-content').empty();

                    if (this.xhr)
                        this.xhr.abort();
                }
                    break;
                case STATE_INITIAL: {
                    this.history = [];

                    // If there's a plugin which has this trigger, then call the plugin
                    if (this.HasTrigger(TRIGGER_EVENT_ON_SET_UP)) {
                        this.ShowLoading(true, LOADING_STATE_INITIAL);

                        this.TriggerEvent(TRIGGER_EVENT_ON_SET_UP).then(function () {
                            $this.ShowIntroduction();

                            $this.$conversations.hide();
                            $this.$introduction.show();

                            $this.ShowLoading(false);

                            $this.$text_prompt_area.focus();
                        });
                    } else {
                        this.$conversations.hide();
                        this.$introduction.show();

                        $this.$text_prompt_area.focus();
                    }
                }
                    break;
                case STATE_CONVERSATION: {
                    this.$introduction.hide();
                    this.$conversations.show();
                    break;
                }
            }

            this.state = stateType;
        };

        this.SendMessage = function (message) {
            if ($this.state !== STATE_CONVERSATION) {
                $this.ShowState(STATE_CONVERSATION);
            }

            this.AddHistory(message, TEXT_PART_ROLE_USER);
            this.AddConversation(message, TEXT_PART_ROLE_USER);
            this.ShowLoading(true);

            this.Send(function (data) {
                $this.AddConversation(data.part.content, data.part.role);
                $this.ShowLoading(false);
            }, function() {
                $this.AddConversation(lmsg('common.ai.chat.error'), TEXT_PART_ROLE_MODEL, true);
                $this.ShowLoading(false);
            });
        };

        this.Send = function (onSuccess, onFail) {
            this.xhr = $.ajax({
                url: '/api/ai/generate-chat',
                method: 'POST',
                dataType: 'json',
                data: {
                    language: this.language,
                    module: this.module,
                    metadata: this.metadata,
                    history: this.history.map(function (part) {
                        return part.serialise();
                    })
                },
                success: function (data) {
                    $this.AddHistory(data.part.content, data.part.role);

                    if (onSuccess) {
                        onSuccess(data);
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    if (textStatus === 'abort') {
                        return;
                    }

                    if (onFail) {
                        onFail();
                    }
                }
            });

            return this.xhr;
        };

        this.isTextLargerThanParent = function (text) {
            var $tempElement = $('<span>')
                .text(cla.stripHTML(text))
                // Add the same amount of padding as the conversation template
                // So we can correctly get the width
                .addClass('p-2 px-3')
                .css({
                    'visibility': 'hidden',
                    'position': 'absolute',
                    'white-space': 'nowrap',
                });

            // The conversation element might be hidden (e.g., display: none or removed from layout),
            // so we temporarily make it visible to accurately measure its width.
            const originalStyle = {
                visibility: this.$conversations.css('visibility'),
                display: this.$conversations.css('display')
            };

            this.$conversations.css({
                visibility: 'hidden',
                display: 'block'
            });

            var $parent = this.$conversations.find('.ai-conversations-content');

            $parent.append($tempElement);

            var textWidth = $tempElement[0].getBoundingClientRect().width;
            var parentWidth = $parent[0].getBoundingClientRect().width;

            $tempElement.remove();

            this.$conversations.css(originalStyle);

            return textWidth >= parentWidth;
        };


        this.AddConversation = function (text, role, error) {
            var text_larger_than_parent = this.isTextLargerThanParent(text);
            var displayClass            = text_larger_than_parent ? 'd-block' : 'd-inline';
            var textAlign               = role == TEXT_PART_ROLE_USER ? (text_larger_than_parent ? 'text-left' : 'text-right') : 'text-left';

            this.$conversations.find('.ai-conversations-content').append(conversation_template({
                text: text,
                role: role,
                displayClass: displayClass,
                textAlign: textAlign,
                isError: error
            }))

            this.ScrollToLatestMessage();
        };

        this.Listeners = function () {
            this.$trigger_button.on('click', function (e) {
                var is_opened = $this.$action_sheet.hasClass(CLASS_AI_ACTION_SHEET_OPENED);
                $(this).tooltip('hide');

                $this.ShowState(STATE_RESET);

                if (!is_opened) {
                    $this.$action_sheet.addClass(CLASS_AI_ACTION_SHEET_OPENED);

                    $this.ShowState(STATE_INITIAL);
                } else {
                    $this.$action_sheet.removeClass(CLASS_AI_ACTION_SHEET_OPENED);
                }
            });

            this.$modal.find('.ai-close').on('click', function () {
                $this.ShowState(STATE_RESET);
                $this.$action_sheet.removeClass(CLASS_AI_ACTION_SHEET_OPENED);
            });

            this.$text_prompt_area.on('keyup', function () {
                $this.ShowValidationError();
            });

            this.$form.on('submit', function (e) {
                e.preventDefault();

                if ($this.IsLoading()) {
                    return;
                }

                $this.feedback_touched = true;

                if (!$this.IsValidationValid()) {
                    $this.ShowValidationError();
                    return;
                }

                var formData = $(this).serializeArray();

                var formObject = {};
                $(formData).each(function (_, obj) {
                    formObject[obj.name] = obj.value;
                });

                if (formObject.question.length == 0) {
                    return;
                }

                $this.SendMessage(cla.stripHTML($this.$text_prompt_area.val()));

                $(this)[0].reset();
            });
        };

        this.GetValidationErrorMessage = function () {
            if (this.$text_prompt_area.val().length == 0) {
                return '';
            }

            if (this.$text_prompt_area.val().length < 3) {
                return lmsg('common.ai.prompt.validation.more_than', 3);
            }

            if (this.$text_prompt_area.val().length > 1000) {
                return lmsg('common.ai.prompt.validation.less_than', 1000);
            }

            return '';
        };

        this.DisableForm = function (toggle) {
            this.$form.find('button').attr('disabled', toggle);
            this.$form.find('input').attr('disabled', toggle);
        }

        this.SetMetaData = function (metadata) {
            this.metadata = metadata;
        }

        this.ShowValidationError = function () {
            // Don't show validation on the first attempt
            if (!this.feedback_touched) {
                return;
            }

            var $feed_back    = this.$modal.find('.invalid-feedback');
            var error_message = this.GetValidationErrorMessage();

            $feed_back.text(error_message);

            if (error_message) {
                this.$text_prompt_area.addClass('is-invalid');
            } else {
                this.$text_prompt_area.removeClass('is-invalid');
            }
        };

        this.IsValidationValid = function () {
            return this.GetValidationErrorMessage() == '';
        };

        this.ResetValidation = function () {
            this.feedback_touched = false;
            this.$text_prompt_area.removeClass('is-invalid');
        };

        this.ScrollToLatestMessage = function () {
            var $container        = this.$conversations.find('.ai-conversations-content');
            var $lastConversation = $container.find('.ai-conversation').last();

            // Check the height of the last message and the container
            var lastConversationHeight = $lastConversation.outerHeight(true);
            var containerHeight        = $container.height();

            if (lastConversationHeight > containerHeight) {
                // If the last message is higher than the container, scroll to the top
                $container.scrollTop(0);
            } else {
                // Otherwise, scroll to the bottom of the chat
                var offset = $lastConversation.position().top + $container.scrollTop();
                $container.scrollTop(offset);
            }
        };

        this.TriggerEvent = function (eventName, eventData) {
            for (var i = 0; i < this.plugins.length; i++) {
                var plugin = this.plugins[i];

                if (typeof plugin[eventName] === 'function') {
                    return plugin[eventName](eventData);
                }
            }

            return null;
        };

        this.HasTrigger = function (eventName) {
            for (var i = 0; i < this.plugins.length; i++) {
                var plugin = this.plugins[i];

                if (typeof plugin[eventName] === 'function') {
                    return true;
                }
            }

            return false;
        };

        this.AddHistory = function (text, role) {
            this.history.push(new TextPart(text, role));
        }

        this.AddFilePart = function (mimeType, data) {
            this.history.push(new FilePart(mimeType, data))
        }


        var extraPadding = true;

        function updateAiAssistantPanelOffset() {
            var navbar = document.querySelector('.claro-navbar');
            var aiPanel = document.querySelector('.ai-action-sheet');
            var mainContainer = document.querySelector('.main-container');

            if (navbar && aiPanel && mainContainer) {
                var height = navbar.offsetHeight;
                var remPx  = parseFloat(getComputedStyle(document.documentElement).fontSize);

                var navbarOffset     = height -1 + (extraPadding ? remPx + 1 : 0);

                var viewportWidth        = document.documentElement.clientWidth;
                var containerRightOffset = viewportWidth - mainContainer.getBoundingClientRect().right;

                aiPanel.style.setProperty('--ai-assistant-offset-top', navbarOffset + 'px');
                aiPanel.style.setProperty('--ai-assistant-offset-right', containerRightOffset + 'px');
            }
        }

        // Scroll listener to toggle extra padding
        function handleScroll() {
            var shouldHavePadding = window.scrollY === 0;
            if (shouldHavePadding !== extraPadding) {
                extraPadding = shouldHavePadding;
                updateAiAssistantPanelOffset();
            }
        }

        // Set on load and on resize
        updateAiAssistantPanelOffset();
        window.addEventListener('scroll', handleScroll);
        window.addEventListener('resize', updateAiAssistantPanelOffset);
        window.addEventListener('navbarHeightChanged', function(event){
            updateAiAssistantPanelOffset();
        });
    };

    return new AiChatConversation();
});
