// Written by retoor@molodetz.nl

// This class defines a custom HTML element that displays a list of messages with avatars and timestamps. It handles message addition with a delay in event dispatch and ensures the display of messages in the correct format.

// The code seems to rely on some external dependencies like 'models.Message', 'app', and 'Schedule'. These should be imported or defined elsewhere in your application.

// MIT License: This is free software. Permission is granted to use, copy, modify, and/or distribute this software for any purpose with or without fee. The software is provided "as is" without any warranty.
import {app} from "./app.js";

const LONG_TIME = 1000 * 60 * 20

class MessageElement extends HTMLElement {
  static observedAttributes = ['data-uid', 'data-color', 'data-channel_uid', 'data-user_nick', 'data-created_at', 'data-user_uid'];

  isVisible() {
    if (!this) return false;
    const rect = this.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }

  updateUI() {
    if (this._originalChildren === undefined) {
      const { color,  user_nick, created_at, user_uid} = this.dataset;
      this.classList.add('message');
      this.style.maxWidth = '100%';
      this._originalChildren = Array.from(this.children);
      this.innerHTML = `
        <a class="avatar" style="background-color: ${color || ''}; color: black;" href="/user/${user_uid || ''}.html">
          <img class="avatar-img" width="40" height="40" src="/avatar/${user_uid || ''}.svg" alt="${user_nick || ''}" loading="lazy">
        </a>
        <div class="message-content">
          <div class="author" style="color: ${color || ''};">${user_nick || ''}</div>
          <div class="text"></div>
          <div class="time no-select" data-created_at="${created_at || ''}">
            <span></span>
            <a href="#reply">reply</a></div>
          </div>
    `;

      this.messageDiv = this.querySelector('.text');

      if (this._originalChildren && this._originalChildren.length > 0) {
          this._originalChildren.forEach(child => {
              this.messageDiv.appendChild(child);
          });
      }

      this.timeDiv = this.querySelector('.time span');
    }

    if (!this.siblingGenerated && this.nextElementSibling) {
      this.siblingGenerated = true;
      if (this.nextElementSibling?.dataset?.user_uid !== this.dataset.user_uid) {
        this.classList.add('switch-user');
      } else {
        this.classList.remove('switch-user');
        const siblingTime = new Date(this.nextElementSibling.dataset.created_at);
        const currentTime = new Date(this.dataset.created_at);

        if (currentTime.getTime()  - siblingTime.getTime() > LONG_TIME) {
          this.classList.add('long-time');
        } else {
          this.classList.remove('long-time');
        }
      }
    }

    this.timeDiv.innerText = app.timeDescription(this.dataset.created_at);
  }

  updateMessage(...messages) {
    if (this._originalChildren) {
      this.messageDiv.replaceChildren(...messages)
      this._originalChildren = messages;
    }
  }

  connectedCallback() {
    this.updateUI();
  }

  disconnectedCallback() {
  }

  connectedMoveCallback() {
  }

  attributeChangedCallback(name, oldValue, newValue) {
    this.updateUI()
  }
}

class MessageList extends HTMLElement {
  constructor() {
    super();
    app.ws.addEventListener("update_message_text", (data) => {
      this.upsertMessage(data);
    });
    app.ws.addEventListener("set_typing", (data) => {
      this.triggerGlow(data.user_uid,data.color);
    });

    this.messageMap = new Map();
    this.visibleSet = new Set();
    this._observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            this.visibleSet.add(entry.target);
            const messageElement = entry.target;
            if (messageElement instanceof MessageElement) {
                messageElement.updateUI();
            }
          } else {
            this.visibleSet.delete(entry.target);
          }
        });
            console.log(this.visibleSet);
    }, {
        root: this,
        threshold: 0.1
    })

    for(const c of this.children) {
      this._observer.observe(c);
        if (c instanceof MessageElement) {
            this.messageMap.set(c.dataset.uid, c);
        }
    }
    this.scrollToBottom(true);
  }

  connectedCallback() {
    this.addEventListener('click', (e) => {
      if (e.target.tagName !== 'IMG' || e.target.classList.contains('avatar-img')) return;

      const img = e.target;

      const overlay = document.createElement('div');
      overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);display:flex;justify-content:center;align-items:center;z-index:9999;cursor:pointer;';

      const urlObj = new URL(img.currentSrc || img.src, window.location.origin);
      urlObj.searchParams.delete('width');
      urlObj.searchParams.delete('height');

      const fullImg = document.createElement('img');
      fullImg.src = urlObj.toString();
      fullImg.alt = img.alt || '';
      fullImg.style.maxWidth = '90%';
      fullImg.style.maxHeight = '90%';
      fullImg.style.boxShadow = '0 0 32px #000';
      fullImg.style.borderRadius = '8px';
      fullImg.style.background = '#222';
      fullImg.style.objectFit = 'contain';
      fullImg.loading = 'lazy';

      overlay.appendChild(fullImg);
      document.body.appendChild(overlay);
      overlay.addEventListener('click', () => {
        if (overlay.parentNode) {
          overlay.parentNode.removeChild(overlay);
        }
      });
      // Optional: ESC key closes overlay
      const escListener = (evt) => {
        if (evt.key === 'Escape') {
          if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
          document.removeEventListener('keydown', escListener);
        }
      };
      document.addEventListener('keydown', escListener);
    })

  }
  isElementVisible(element) {
    if (!element) return false;
    const rect = element.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }
  isScrolledToBottom() {
    return this.isElementVisible(this.firstElementChild);
  }
  scrollToBottom(force = false, behavior= 'smooth') {
    if (force || this.isScrolledToBottom()) {
      this.firstElementChild.scrollIntoView({ behavior, block: 'start' });
      setTimeout(() => {
        this.firstElementChild.scrollIntoView({ behavior, block: 'start' });
      }, 200);
    }
  }

  triggerGlow(uid, color) {
    if (!uid || !color) return;
    app.starField.glowColor(color);
    let lastElement = null;
    this.querySelectorAll('.avatar').forEach((el) => {
      const anchor = el.closest('a');
      if (anchor && typeof anchor.href === 'string' && anchor.href.includes(uid)) {
        lastElement = el;
      }
    });
    if (lastElement) {
      lastElement.classList.add('glow');
      setTimeout(() => {
        lastElement.classList.remove('glow');
      }, 1000);
    }
  }


  updateTimes() {
    this.visibleSet.forEach((messageElement) => {
        if (messageElement instanceof MessageElement) {
            messageElement.updateUI();
        }
    })
  }

  upsertMessage(data) {
    let message = this.messageMap.get(data.uid);
    const newMessage = !!message;
    if (message) {
        message.parentElement.removeChild(message);
    }

    if (!data.message) return

    const wrapper = document.createElement("div");

    wrapper.innerHTML = data.html;

    if (message) {
      message.updateMessage(...wrapper.firstElementChild._originalChildren);
    } else {
        message = wrapper.firstElementChild;
        this.messageMap.set(data.uid, message);
        this._observer.observe(message);
    }

    const scrolledToBottom = this.isScrolledToBottom();
    this.prepend(message);
    if (scrolledToBottom) this.scrollToBottom(true, !newMessage ? 'smooth' : 'auto');
  }
}

customElements.define("chat-message", MessageElement);
customElements.define("message-list", MessageList);
