(function () {
  function parseJson(value) {
    try {
      return JSON.parse(value);
    } catch (err) {
      return [];
    }
  }

  function createEl(tag, className, text) {
    const el = document.createElement(tag);
    if (className) el.className = className;
    if (text !== undefined && text !== null) el.textContent = text;
    return el;
  }

  function createMetaItem(iconClass, value) {
    const item = createEl("span", "kanban-meta-item");
    const icon = document.createElement("i");
    icon.className = `fa fa-${iconClass}`;
    const text = createEl(
      "span",
      "kanban-meta-value",
      String(value !== undefined && value !== null ? value : 0)
    );
    item.appendChild(icon);
    item.appendChild(text);
    return item;
  }

  function buildCard(item, tone) {
    const card = document.createElement("div");
    card.className = `kanban-card kanban-tone-${tone || "slate"}`;
    card.dataset.id = item.id !== undefined && item.id !== null ? String(item.id) : "";
    card.dataset.status =
      item.status !== undefined && item.status !== null ? String(item.status) : "";
    card.draggable = true;

    const header = createEl("div", "kanban-card-header");
    const id = createEl("span", "kanban-card-id", item.id ? `#${item.id}` : "");
    const status = createEl("span", "kanban-chip", item.status_label || "");
		const statusTone = item.status_tone || tone || "slate";		
		status.classList.add(`!bg-${statusTone}-500`, "!text-white");
    if (!item.status_label) status.classList.add("is-empty");
    header.appendChild(id);
    header.appendChild(status);
    card.appendChild(header);

    const title = createEl("div", "kanban-card-title");
    if (item.link) {
      const link = createEl("a", "kanban-card-link", item.title || "");
      link.href = item.link;
      link.rel = "noopener";
      title.appendChild(link);
    } else {
      title.textContent = item.title || "";
    }
    card.appendChild(title);

    if (item.subtitle) {
      card.appendChild(createEl("div", "kanban-card-subtitle", item.subtitle));
    }

    const progressValue = Math.max(0, Math.min(100, Number(item.progress) || 0));
    const progress = createEl("div", "kanban-progress");
    const bar = createEl("div", "kanban-progress-bar");
    const fill = createEl("div", "kanban-progress-fill");
    fill.style.width = `${progressValue}%`;
    bar.appendChild(fill);
    const label = createEl("span", "kanban-progress-label", `${progressValue}%`);
    progress.appendChild(bar);
    progress.appendChild(label);
    card.appendChild(progress);

    const meta = createEl("div", "kanban-card-meta");
    meta.appendChild(createMetaItem("comment-o", item.comments));
    meta.appendChild(createMetaItem("paperclip", item.attachments));
    if (item.link) {
      const action = createEl("a", "kanban-card-action", "Open");
      action.href = item.link;
      action.rel = "noopener";
      meta.appendChild(action);
    }
    card.appendChild(meta);

    return card;
  }

  function buildColumn(column, items) {
    const columnEl = document.createElement("div");
    columnEl.className = `kanban-column kanban-tone-${column.tone || "slate"}`;
    columnEl.dataset.columnId = String(
      column.id !== undefined && column.id !== null ? column.id : ""
    );
    columnEl.dataset.tone = column.tone || "slate";
    if (column.total !== undefined && column.total !== null) {
      columnEl.dataset.total = String(column.total);
    }

    const header = createEl("div", "kanban-column-header");
    const titleWrap = createEl("div", "kanban-column-title-wrap");
    titleWrap.appendChild(createEl("span", "kanban-column-title", column.label || ""));
    if (column.subtitle) {
      titleWrap.appendChild(createEl("span", "kanban-column-subtitle", column.subtitle));
    }
    const count = createEl("span", "kanban-count", String(items.length));
    header.appendChild(titleWrap);
    header.appendChild(count);

    const body = createEl("div", "kanban-column-body");
    body.dataset.columnId = String(
      column.id !== undefined && column.id !== null ? column.id : ""
    );
    items.forEach((item) => body.appendChild(buildCard(item, column.tone)));

    columnEl.appendChild(header);
    columnEl.appendChild(body);
    return columnEl;
  }

  function updateColumnCount(columnEl) {
    const countEl = columnEl.querySelector(".kanban-count");
    if (!countEl) return;
    const total = columnEl.querySelectorAll(".kanban-card").length;
    countEl.textContent = String(total);
  }

  function getCookieValue(name) {
    if (!document.cookie) return "";
    const cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i += 1) {
      const cookie = cookies[i].trim();
      if (cookie.substring(0, name.length + 1) === `${name}=`) {
        return decodeURIComponent(cookie.substring(name.length + 1));
      }
    }
    return "";
  }

  function getDragAfterElement(container, y) {
    const draggableElements = [].slice.call(
      container.querySelectorAll(".kanban-card:not(.is-dragging)")
    );
    return draggableElements.reduce(
      (closest, child) => {
        const box = child.getBoundingClientRect();
        const offset = y - box.top - box.height / 2;
        if (offset < 0 && offset > closest.offset) {
          return { offset, element: child };
        }
        return closest;
      },
      { offset: Number.NEGATIVE_INFINITY, element: null }
    ).element;
  }

  function sendOrder(columnEl, sortUrlBase) {
    if (!sortUrlBase || !columnEl) return;
    const columnId = columnEl.dataset.columnId;
    if (columnId === undefined || columnId === null) return;

    const taskIds = []
      .slice.call(columnEl.querySelectorAll(".kanban-card"))
      .map((card) => card.dataset.id)
      .filter(Boolean);
    if (!taskIds.length) return;

    const payload = new URLSearchParams();
    taskIds.forEach((id) => payload.append("task_id[]", id));

    const csrfToken = getCookieValue("csrf_cookie");
    if (csrfToken) {
      payload.append("csrf_token", csrfToken);
    }

    fetch(`${sortUrlBase}${columnId}`, {
      method: "POST",
      credentials: "same-origin",
      headers: { "X-Requested-With": "XMLHttpRequest" },
      body: payload,
    }).catch(() => {});
  }

  function bindCardDrag(card, dragState) {
    if (card.dataset.dndBound === "true") return;
    card.dataset.dndBound = "true";
    card.addEventListener("dragstart", (event) => {
      dragState.card = card;
      dragState.sourceColumn = card.closest(".kanban-column");
      card.classList.add("is-dragging");
      event.dataTransfer.effectAllowed = "move";
      event.dataTransfer.setData("text/plain", card.dataset.id || "");
    });

    card.addEventListener("dragend", () => {
      card.classList.remove("is-dragging");
      dragState.card = null;
      dragState.sourceColumn = null;
    });
  }

  function bindColumnDrop(columnBody, dragState, sortUrlBase) {
    if (columnBody.dataset.dndBound === "true") return;
    columnBody.dataset.dndBound = "true";
    columnBody.addEventListener("dragover", (event) => {
      event.preventDefault();
      const dragging = dragState.card;
      if (!dragging) return;
      const afterElement = getDragAfterElement(columnBody, event.clientY);
      if (afterElement == null) {
        columnBody.appendChild(dragging);
      } else {
        columnBody.insertBefore(dragging, afterElement);
      }
    });

    columnBody.addEventListener("dragenter", () => {
      columnBody.classList.add("is-drag-over");
    });

    columnBody.addEventListener("dragleave", (event) => {
      if (event.relatedTarget && columnBody.contains(event.relatedTarget)) {
        return;
      }
      columnBody.classList.remove("is-drag-over");
    });

    columnBody.addEventListener("drop", (event) => {
      event.preventDefault();
      columnBody.classList.remove("is-drag-over");
      const dragging = dragState.card;
      if (!dragging) return;
      const targetColumn = columnBody.closest(".kanban-column");
      if (!targetColumn) return;

      dragging.dataset.status = targetColumn.dataset.columnId || "";
      updateColumnCount(targetColumn);
      if (
        dragState.sourceColumn &&
        dragState.sourceColumn !== targetColumn
      ) {
        updateColumnCount(dragState.sourceColumn);
        sendOrder(dragState.sourceColumn, sortUrlBase);
      }
      sendOrder(targetColumn, sortUrlBase);
    });
  }

  function initDragAndDrop(rootEl) {
    const sortUrlBase = rootEl.dataset.sortUrlBase || "";
    if (!rootEl._kanbanDragState) {
      rootEl._kanbanDragState = {
        card: null,
        sourceColumn: null,
      };
    }
    const dragState = rootEl._kanbanDragState;

    rootEl.querySelectorAll(".kanban-card").forEach((card) => {
      bindCardDrag(card, dragState);
    });

    rootEl.querySelectorAll(".kanban-column-body").forEach((columnBody) => {
      bindColumnDrop(columnBody, dragState, sortUrlBase);
    });
  }

  function addLoadMoreButton(columnEl, loadedCount, totalCount, loadLabel) {
    if (totalCount <= loadedCount) return;
    const footer = createEl("div", "kanban-column-footer");
    const button = createEl("button", "kanban-load-more", loadLabel);
    button.type = "button";
    button.dataset.columnId = columnEl.dataset.columnId || "";
    button.dataset.loaded = String(loadedCount);
    button.dataset.total = String(totalCount);
    footer.appendChild(button);
    columnEl.appendChild(footer);
  }

  function initLoadMore(rootEl) {
    if (rootEl.dataset.loadMoreInit === "true") return;
    rootEl.dataset.loadMoreInit = "true";

    rootEl.addEventListener("click", (event) => {
      const button = event.target.closest(".kanban-load-more");
      if (!button) return;
      const loadUrlBase = rootEl.dataset.loadUrlBase || "";
      if (!loadUrlBase) return;

      const pageSize = parseInt(rootEl.dataset.pageSize, 10) || 6;
      const loadLabel = rootEl.dataset.loadLabel || "Load more";

      const columnId = button.dataset.columnId;
      const columnEl = rootEl.querySelector(
        `.kanban-column[data-column-id="${columnId}"]`
      );
      if (!columnEl) return;
      const body = columnEl.querySelector(".kanban-column-body");
      if (!body) return;

      const loadedCount = parseInt(button.dataset.loaded, 10) || 0;
      const totalCount = parseInt(button.dataset.total, 10) || 0;
      if (totalCount && loadedCount >= totalCount) {
        button.remove();
        return;
      }

      button.disabled = true;
      button.textContent = "Loading...";

      const payload = new URLSearchParams();
      payload.append("offset", String(loadedCount));
      payload.append("limit", String(pageSize));

      const csrfToken = getCookieValue("csrf_cookie");
      if (csrfToken) {
        payload.append("csrf_token", csrfToken);
      }

      fetch(`${loadUrlBase}${columnId}`, {
        method: "POST",
        credentials: "same-origin",
        headers: { "X-Requested-With": "XMLHttpRequest" },
        body: payload,
      })
        .then((response) => response.json())
        .then((data) => {
          if (!data || !Array.isArray(data.items)) {
            return;
          }
          const dragState = rootEl._kanbanDragState || {
            card: null,
            sourceColumn: null,
          };
          const tone = columnEl.dataset.tone || "slate";
          data.items.forEach((item) => {
            const card = buildCard(item, tone);
            body.appendChild(card);
            bindCardDrag(card, dragState);
          });
          const newLoaded = loadedCount + data.items.length;
          const newTotal =
            data.total !== undefined && data.total !== null
              ? parseInt(data.total, 10) || 0
              : totalCount;
          button.dataset.loaded = String(newLoaded);
          button.dataset.total = String(newTotal);
          updateColumnCount(columnEl);
          if (!newTotal || newLoaded >= newTotal || data.items.length < pageSize) {
            button.remove();
            return;
          }
          button.disabled = false;
          button.textContent = loadLabel;
        })
        .catch(() => {
          button.disabled = false;
          button.textContent = loadLabel;
        });
    });
  }

  function enableWheelScroll(board) {
    if (!board || board.dataset.wheelBound === "true") return;
    board.dataset.wheelBound = "true";
    board.addEventListener(
      "wheel",
      (event) => {
        const maxScroll = board.scrollWidth - board.clientWidth;
        if (maxScroll <= 0) {
          return;
        }
        if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) {
          return;
        }
        const current = board.scrollLeft;
        const next = Math.max(0, Math.min(maxScroll, current + event.deltaY));
        if (next === current) {
          return;
        }
        board.scrollLeft = next;
        event.preventDefault();
      },
      { passive: false }
    );
  }

  function renderKanban(el) {
    const board = el.querySelector(".kanban-board");
    if (!board) return;
    const columns = parseJson(el.dataset.columns || "[]");
    const items = parseJson(el.dataset.items || "[]");
    board.innerHTML = "";

    const groupedItems = items.reduce((acc, item) => {
      const key = String(
        item.status !== undefined && item.status !== null ? item.status : ""
      );
      if (!acc[key]) acc[key] = [];
      acc[key].push(item);
      return acc;
    }, {});

    const loadUrlBase = el.dataset.loadUrlBase || "";
    const loadLabel = el.dataset.loadLabel || "Load more";

    columns.forEach((column) => {
      const key = String(
        column.id !== undefined && column.id !== null ? column.id : ""
      );
      const columnItems = groupedItems[key] || [];
      const columnEl = buildColumn(column, columnItems);
      const totalCount =
        column.total !== undefined && column.total !== null
          ? parseInt(column.total, 10) || 0
          : columnItems.length;
      if (loadUrlBase) {
        addLoadMoreButton(columnEl, columnItems.length, totalCount, loadLabel);
      }
      board.appendChild(columnEl);
    });

    initDragAndDrop(el);
    initLoadMore(el);
    enableWheelScroll(board);
  }

  function initKanban(root = document) {
    const scope = root && typeof root.querySelectorAll === "function" ? root : document;
    scope.querySelectorAll("[data-kanban]").forEach((el) => renderKanban(el));
  }

  window.initKanban = initKanban;

  if (document.readyState === "interactive" || document.readyState === "complete") {
    initKanban();
  } else {
    document.addEventListener("DOMContentLoaded", initKanban);
  }
})();
