const md = window.markdownit({
  linkify: true,
  highlight(code, lang) {
    const language = hljs.getLanguage(lang) ? lang : 'plaintext';
    const html = hljs.highlight(code, {language: language, ignoreIllegals: true }).value
    return `<pre class="hljs-code-container my-3"><div class="hljs-code-header"><span>${language}</span><button class="hljs-copy-button">Copy</button></div><code class="hljs language-${language}">${html}</code></pre>`
  },
});

new ClipboardJS('.hljs-copy-button', {
  target: function(trigger) {
      console.log(trigger.parentNode.nextElementSibling)
      return trigger.parentNode.nextElementSibling;
  }
});

async function getApiUrl() {
  if (getApiUrl.url) return getApiUrl.url;
  try {
    const response = await fetch("/v1/chat/completions", {
      method: "OPTIONS",
    });
    if (response.status !== 200) throw new Error();
    getApiUrl.url = "/v1/chat/completions";
    const corsHeaders = (
      response.headers.get("Access-Control-Allow-Headers") || ""
    ).toLowerCase();
    getApiUrl.tokenRequired = corsHeaders.includes("authorization");
  } catch (e) {
    getApiUrl.url = "https://api.openai.com/v1/chat/completions";
    getApiUrl.tokenRequired = true;
  }
  return getApiUrl.url;
}

async function postRequest(url, headers, body) {
  const response = await fetch(url, {
    method: "POST",
    headers: headers,
    body: JSON.stringify(body),
  });
  if (!response.ok) {
    throw new Error(await response.text());
  }
  return response;
}

async function readStream(stream, progressCallback) {
  const reader = stream.getReader();
  const textDecoder = new TextDecoder('utf-8');
  let responseObj = {};

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    const lines = textDecoder.decode(value).split("\n");
    processLines(lines, responseObj, progressCallback);
  }

  return responseObj;
}

function processLines(lines, responseObj, progressCallback) {
  for (const line of lines) {
    if (line.startsWith("data: ")) {
      if (line.includes("[DONE]")) {
        return responseObj;
      }
      try {
        const data = JSON.parse(line.slice(6));
        const delta = data.choices[0].delta;

        Object.keys(delta).forEach(key => {
          responseObj[key] = (responseObj[key] || "") + delta[key];
          progressCallback(responseObj);
        });

      } catch (e) {
        console.log("Error parsing line:", line);
      }
    }
  }
}

async function complete(messages, token, progressCallback) {
  const apiUrl = await getApiUrl();
  const headers = { "Content-Type": "application/json" };
  if (getApiUrl.tokenRequired) {
    headers.Authorization = `Bearer ${token}`;
  }

  const body = {
    model: "gpt-3.5-turbo",
    messages: messages,
    stream: true,
  };

  const response = await postRequest(apiUrl, headers, body);
  return readStream(response.body, progressCallback);
}

function chatMessage(message) {
  return {
    scrollToBottom() {
      const chatContainer = document.getElementById('chatContainer');
      const main = chatContainer.parentElement;
      main.scrollTop = main.scrollHeight;
    },
    watchEffect(message)
    {
      this.$nextTick(() => {
        this.$el.innerHTML = md.render(message);
        this.scrollToBottom()
      });
    }
  }
}

function chatApp() {
  return {
      messages: [],
      newMessage: '',

      init() {
        this.messages = [
          { role: 'system', content: 'You are a programing assistant. Answer using markdown.' }
        ];

        hljs.configure({
          'cssSelector' : 'pre code'
        });
      },

      sendMessage() {
          if (this.newMessage.trim() === '') return;

          const userMessage = { role: 'user', content: this.newMessage };


          this.messages.push(userMessage);
          this.messages.push({ role: 'assistant', content: '' });
          const lastMsgIndex = this.messages.length - 1;

          try {
            complete(
              this.messages,
              'no-token',
              (message) => {
                if (message.content)
                  this.messages[lastMsgIndex].content = message.content;
              }
            );

          } catch (error) {
            console.log(error.message);
            return;
          } finally {
            this.newMessage = '';
          }
      },

      clearMessages() {
        this.messages = [];
      },
  };
}