import { app } from "../app.js";

class ChatInputComponent extends HTMLElement {
  autoCompletions = {
    "example 1": () => {},
    "example 2": () => {},
  };

  constructor() {
    super();
    this.lastUpdateEvent = new Date();
    this.textarea = document.createElement("textarea");
    this._value = "";
    this.value = this.getAttribute("value") || "";
    this.previousValue = this.value;
    this.lastChange = new Date();
    this.changed = false;
  }

  get value() {
    return this._value;
  }

  set value(value) {
    this._value = value || "";
    this.textarea.value = this._value;
  }

  resolveAutoComplete() {
    let count = 0;
    let value = null;
    Object.keys(this.autoCompletions).forEach((key) => {
      if (key.startsWith(this.value)) {
        count++;
        value = key;
      }
    });
    if (count == 1) return value;
    return null;
  }

  isActive() {
    return document.activeElement === this.textarea;
  }

  focus() {
    this.textarea.focus();
  } 
    getAuthors(){
        let authors = []
        for (let i = 0; i < this.users.length; i++) {
            authors.push(this.users[i].username)
            authors.push(this.users[i].nick)
        }
        return authors
        
    }
 extractMentions(text) {
  const regex = /@([a-zA-Z0-9_-]+)/g;
  const mentions = [];
  let match;

  while ((match = regex.exec(text)) !== null) {
    mentions.push(match[1]);
  }

  return mentions;
}
    matchMentionsToAuthors(mentions, authors) {
  return mentions.map(mention => {
    let closestAuthor = null;
    let minDistance = Infinity;
     const lowerMention = mention.toLowerCase();
    
    authors.forEach(author => {
    const lowerAuthor = author.toLowerCase();
      let distance = this.levenshteinDistance(lowerMention, lowerAuthor);
     

        if(!this.isSubsequence(lowerMention,lowerAuthor)) {
            distance += 10        
        }

      if (distance < minDistance) {
        minDistance = distance;
        closestAuthor = author;

      }

    });

    return { mention, closestAuthor, distance: minDistance };
  });
}
levenshteinDistance(a, b) {
  const matrix = [];

  // Initialize the first row and column
  for (let i = 0; i <= b.length; i++) {
    matrix[i] = [i];
  }
  for (let j = 0; j <= a.length; j++) {
    matrix[0][j] = j;
  }

  // Fill in the matrix
  for (let i = 1; i <= b.length; i++) {
    for (let j = 1; j <= a.length; j++) {
      if (b.charAt(i - 1) === a.charAt(j - 1)) {
        matrix[i][j] = matrix[i - 1][j - 1];
      } else {
        matrix[i][j] = Math.min(
          matrix[i - 1][j] + 1,    // Deletion
          matrix[i][j - 1] + 1,    // Insertion
          matrix[i - 1][j - 1] + 1 // Substitution
        );
      }
    }
  }

  return matrix[b.length][a.length];
}



      replaceMentionsWithAuthors(text) {
    const authors = this.getAuthors();
  const mentions = this.extractMentions(text);
  
  const matches = this.matchMentionsToAuthors(mentions, authors);
  let updatedText = text;
  matches.forEach(({ mention, closestAuthor }) => {
    const mentionRegex = new RegExp(`@${mention}`, 'g');
      updatedText = updatedText.replace(mentionRegex, `@${closestAuthor}`);
  });
  
  return updatedText;
}



  async connectedCallback() {
    this.user = null 
    app.rpc.getUser(null).then((user) => {
        this.user=user
    })


      const me = this;
    this.liveType = this.getAttribute("live-type") === "true";
    this.liveTypeInterval =
      parseInt(this.getAttribute("live-type-interval")) || 3;
    this.channelUid = this.getAttribute("channel");
    
    app.rpc.getRecentUsers(this.channelUid).then(users=>{
        this.users = users    
    })
      this.messageUid = null;

    this.classList.add("chat-input");

    this.textarea.setAttribute("placeholder", "Type a message...");
    this.textarea.setAttribute("rows", "2");

    this.appendChild(this.textarea);

    this.uploadButton = document.createElement("upload-button");
    this.uploadButton.setAttribute("channel", this.channelUid);
    this.uploadButton.addEventListener("upload", (e) => {
      this.dispatchEvent(new CustomEvent("upload", e));
    });
    this.uploadButton.addEventListener("uploaded", (e) => {
      this.dispatchEvent(new CustomEvent("uploaded", e));
    });

    this.appendChild(this.uploadButton);

    this.textarea.addEventListener("keyup", (e) => {
      if (e.key === "Enter" && !e.shiftKey) {
        this.value = "";
        e.target.value = "";
        return;
      }
      this.value = e.target.value;
      this.changed = true;
      this.update();
    });

    this.textarea.addEventListener("keydown", (e) => {
      this.value = e.target.value;
      if (e.key === "Tab") {
        e.preventDefault();
        let autoCompletion = this.resolveAutoComplete();
        if (autoCompletion) {
          e.target.value = autoCompletion;
          this.value = autoCompletion;
          return;
        }
      }
      if (e.key === "Enter" && !e.shiftKey) {
        e.preventDefault();

        const message = me.replaceMentionsWithAuthors(this.value);
        e.target.value = "";

        if (!message) {
          return;
        }

        let autoCompletion = this.autoCompletions[message];
        if (autoCompletion) {
          this.value = "";
          this.previousValue = "";
          e.target.value = "";
          autoCompletion();
          return;
        }

        this.updateMessage()
        app.rpc.finalizeMessage(this.messageUid)
        this.value = "";
        this.previousValue = "";
        this.messageUid = null;
      }
    });

    this.changeInterval = setInterval(() => {
      if (!this.liveType) {
        return;
      }
      if (this.value !== this.previousValue) {
        if (
          this.trackSecondsBetweenEvents(this.lastChange, new Date()) >=
          this.liveTypeInterval
        ) {
          this.value = "";
          this.previousValue = "";
        }
        this.lastChange = new Date();
      }
      this.update();
    }, 300);

    this.addEventListener("upload", (e) => {
      this.focus();
    });
    this.addEventListener("uploaded", function (e) {
      let message = "";
      e.detail.files.forEach((file) => {
        message += `[${file.name}](/channel/attachment/${file.relative_url})`;
      });
      app.rpc.sendMessage(this.channelUid, message,true);
    });
    setTimeout(()=>{
      this.focus();
    },1000)
  }

  trackSecondsBetweenEvents(event1Time, event2Time) {
    const millisecondsDifference = event2Time.getTime() - event1Time.getTime();
    return millisecondsDifference / 1000;
  }
  isSubsequence(s, t) {
      let i = 0, j = 0;
      while (i < s.length && j < t.length) {
        if (s[i] === t[j]) {
          i++;
        }
        j++;
      }
      return i === s.length; 
    }


  newMessage() {
    if (!this.messageUid) {
      this.messageUid = "?";
    }

    this.value = this.replaceMentionsWithAuthors(this.value);
    this.sendMessage(this.channelUid, this.value,!this.liveType).then((uid) => {
        if (this.liveType) {
            this.messageUid = uid;
        }
    });
    }

  updateMessage() {
    if (this.value[0] == "/") {
      return false;
    }
    if (!this.messageUid) {
      this.newMessage();
      return false;
    }
    if (this.messageUid === "?") {
      return false;
    }
    if (
      typeof app !== "undefined" &&
      app.rpc &&
      typeof app.rpc.updateMessageText === "function"
    ) {
      app.rpc.updateMessageText(this.messageUid, this.replaceMentionsWithAuthors(this.value));
    }
  }

  updateStatus() {
    if (this.liveType) {
      return;
    }
    if (this.trackSecondsBetweenEvents(this.lastUpdateEvent, new Date()) > 1) {
      this.lastUpdateEvent = new Date();
      if (
        typeof app !== "undefined" &&
        app.rpc &&
        typeof app.rpc.set_typing === "function"
      ) {
        app.rpc.set_typing(this.channelUid, this.user.color);
      }
    }
  }

  update() {
    const expired =
      this.trackSecondsBetweenEvents(this.lastChange, new Date()) >=
      this.liveTypeInterval;
    const changed = this.value !== this.previousValue;

    if (changed || expired) {
      this.lastChange = new Date();
      this.updateStatus();
    }

    this.previousValue = this.value;

    if (this.liveType && expired) {
      this.value = "";
      this.previousValue = "";
      this.messageUid = null;
      return;
    }

    if (changed) {
      if (this.liveType) {
        this.updateMessage();
      }
    }
  }

  async sendMessage(channelUid, value,is_final) {
    if (!value.trim()) {
      return null;
    }
    return await app.rpc.sendMessage(channelUid, value,is_final);
  }
}

customElements.define("chat-input", ChatInputComponent);
