/*-
 * #%L
 * Chat Assistant Add-on
 * %%
 * Copyright (C) 2023 - 2025 Flowing Code
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.flowingcode.vaadin.addons.chatassistant;

import com.flowingcode.vaadin.addons.demo.DemoSource;
import com.flowingcode.vaadin.addons.demo.SourcePosition;
import com.google.common.base.Strings;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.avatar.Avatar;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.icon.SvgIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

@DemoSource(sourcePosition = SourcePosition.PRIMARY)
@PageTitle("Generative Answer Demo")
@SuppressWarnings("serial")
@Route(value = "chat-assistant/generative-demo", layout = ChatAssistantDemoView.class)
@CssImport("./styles/chat-assistant-styles-demo.css")
public class ChatAssistantGenerativeDemo extends VerticalLayout {
  
  public ChatAssistantGenerativeDemo() {
    String sampleText = "Hi, I'm an advanced language model. I'm here to help you demonstrate"
        + " how a text-streaming chat component works in Vaadin. As you can see, each word appears"
        + " with a slight pause, simulating the time it would take me to \"think\" and generate"
        + " the next word. I hope this is useful for your demonstration!";
    
    ChatAssistant<CustomMessage> chatAssistant = new ChatAssistant<>();
    SvgIcon icon = new SvgIcon("chatbot.svg");
    icon.setColor("var(--lumo-primary-contrast-color)");
    chatAssistant.setFabIcon(icon);
    chatAssistant.setWindowWidth("400px");
    chatAssistant.setWindowHeight("400px");
    TextArea message = new TextArea();
    message.setLabel("Enter a message from the assistant");
    message.setSizeFull();
    message.setValue(sampleText);
    message.addKeyPressListener(ev->{
      if (Strings.isNullOrEmpty(chatAssistant.getWhoIsTyping())) {
        chatAssistant.setWhoIsTyping("Assistant is generating an answer ...");
      }
    });
    message.addBlurListener(ev->chatAssistant.clearWhoIsTyping());
    chatAssistant.setMessagesRenderer(new ComponentRenderer<CustomChatMessage,CustomMessage>(m -> {
      return new CustomChatMessage(m); 
    },
        (component, m) -> {
          ((CustomChatMessage) component).setMessage(m);
          return component;
        }));
    chatAssistant.setSubmitListener(se -> {
      chatAssistant.sendMessage(CustomMessage.builder().messageTime(LocalDateTime.now())
          .name("User").content(se.getValue()).tagline("Generated by user").build());
    });

    Button chat = new Button("Chat");
    chat.addClickListener(ev -> {
      CustomMessage m = CustomMessage.builder().content(message.getValue()).messageTime(LocalDateTime.now())
          .name("Assistant").avatar("chatbot.png").tagline("Generated by assistant").build();

      chatAssistant.sendMessage(m);
      message.clear();
    });
    Button chatWithThinking = new Button("Chat With Generative Thinking");
    chatWithThinking.addClickListener(ev -> {
      String messageToSend = message.getValue();
      CustomMessage delayedMessage = CustomMessage.builder().loading(true).content("")
          .messageTime(LocalDateTime.now())
          .name("Assistant").avatar("chatbot.png").tagline("Generated by assistant").build();

      UI currentUI = UI.getCurrent();
      chatAssistant.sendMessage(delayedMessage);

      CompletableFuture.runAsync(() -> {
        try {
          TimeUnit.MILLISECONDS.sleep(500);
          currentUI.access(() -> {
            delayedMessage.setLoading(false);
          });
          streamWords(messageToSend)
          .forEach(item -> {
            currentUI.access(() -> {
              delayedMessage.setContent(delayedMessage.getContent() + item);
              chatAssistant.updateMessage(delayedMessage); 
            });
          });
        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        }
      });

      message.clear();
    });
    chatAssistant.sendMessage(CustomMessage.builder().content("Hello, I am here to assist you")
        .messageTime(LocalDateTime.now())
        .name("Assistant").avatar("chatbot.png").tagline("Generated by assistant").build());
    
    add(message, chat, chatWithThinking, chatAssistant);
  }
  
  public Stream<String> streamWords(String fullText) {
    if (fullText == null || fullText.isEmpty()) {
        return Stream.empty();
    }

    String[] words = fullText.split("\\s+");

    return Arrays.stream(words)
        .map(word -> {
            try {
                long delay = ThreadLocalRandom.current().nextLong(50, 250);
                Thread.sleep(delay);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); 
            }
            
            return word + " ";
        });
}
  
}
