"use strict";

const POLL_MS = 2000;
const $ = (id) => document.getElementById(id);
const state = {
  view: "feed",
  activeThread: "",
  activeThreadName: "Messages",
};

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

function ageLabel(seconds) {
  const value = Math.max(0, Number(seconds) || 0);
  if (value < 60) return "now";
  if (value < 3600) return Math.floor(value / 60) + "m";
  if (value < 86400) return Math.floor(value / 3600) + "h";
  return Math.floor(value / 86400) + "d";
}

async function requestJson(path, options) {
  const response = await fetch(path, options);
  if (!response.ok) throw new Error("HTTP " + response.status);
  return response.json();
}

function setOnline(online) {
  const el = $("connection");
  el.textContent = online ? "Online" : "Reconnecting";
  el.classList.toggle("online", online);
}

function showEmpty(container, message) {
  container.replaceChildren(node("p", "empty", message));
}

async function refreshPlayerState() {
  const data = await requestJson("/api/state");
  $("player-name").textContent = data.playerName || "@choppa_frank";
  $("money").textContent = "$" + Number(data.money || 0).toLocaleString();
  $("zone").textContent = (data.zone || "Unknown") + (data.zoneHot ? " HOT" : "");
  $("wanted").textContent = Number(data.wanted || 0) + " stars";
  $("profiles").textContent = Number(data.activeSuspectProfiles || 0) + " active";
}

async function refreshFeed() {
  const list = $("feed-list");
  const data = await requestJson("/api/feed");
  const tweets = Array.isArray(data.tweets) ? data.tweets : [];
  if (!tweets.length) {
    showEmpty(list, "No chatter yet. The feed will update as the block reacts.");
    return;
  }

  const fragment = document.createDocumentFragment();
  tweets.forEach((tweet) => {
    const card = node("article", "tweet" + (tweet.isPlayer ? " player" : ""));
    const head = node("div", "tweet-head");
    const identity = node("div");
    identity.append(node("strong", "", tweet.name || tweet.handle || "Unknown"));
    identity.append(node("span", "handle", " " + (tweet.handle || "")));
    head.append(identity, node("span", "meta", ageLabel(tweet.ageSec)));
    card.append(head, node("p", "body", tweet.body || ""));
    if (tweet.photoUrl) {
      const frame = node("figure", "tweet-photo-frame");
      const img = node("img", "tweet-photo");
      img.src = tweet.photoUrl;
      img.alt = "Attached photo";
      img.loading = "lazy";
      frame.append(img, node("figcaption", "tweet-photo-caption", "Captured in-game"));
      card.append(frame);
    }
    card.append(node("p", "tweet-foot", String(Number(tweet.likes || 0)) + " likes"));
    fragment.append(card);
  });
  list.replaceChildren(fragment);
}

async function refreshDmList() {
  const list = $("dm-list");
  const data = await requestJson("/api/dms");
  const threads = Array.isArray(data.threads) ? data.threads : [];
  if (!threads.length) {
    showEmpty(list, "No messages yet. New contacts will show up here.");
    return;
  }

  const fragment = document.createDocumentFragment();
  threads.forEach((thread) => {
    const card = node("button", "dm-card");
    card.type = "button";
    card.addEventListener("click", () => openThread(thread.fingerprint, thread.name));
    const head = node("div", "dm-head");
    head.append(node("strong", "", thread.name || "Unknown"));
    if (Number(thread.unread || 0) > 0) head.append(node("span", "badge", String(thread.unread)));
    card.append(head);
    card.append(node("p", "preview", thread.preview || "Open conversation"));
    card.append(node("p", "meta", ageLabel(thread.lastSentAgeSec)));
    fragment.append(card);
  });
  list.replaceChildren(fragment);
}

async function refreshThread() {
  if (!state.activeThread) return;
  const data = await requestJson("/api/dms/" + encodeURIComponent(state.activeThread));
  const list = $("dm-thread");
  const messages = Array.isArray(data.messages) ? data.messages : [];
  if (!messages.length) {
    showEmpty(list, "No messages in this conversation yet.");
    return;
  }

  const fragment = document.createDocumentFragment();
  messages.forEach((message) => {
    const card = node("article", "message" + (message.fromPlayer ? " player" : ""));
    card.append(node("span", "", message.text || ""));
    card.append(node("span", "meta", ageLabel(message.ageSec)));
    fragment.append(card);
  });
  list.replaceChildren(fragment);
  list.scrollTop = list.scrollHeight;
}

async function openThread(fingerprint, name) {
  if (!fingerprint) return;
  state.activeThread = fingerprint;
  state.activeThreadName = name || "Messages";
  $("dm-thread-name").textContent = state.activeThreadName;
  $("dm-list-screen").classList.add("hidden");
  $("dm-thread-screen").classList.remove("hidden");
  await guarded(refreshThread);
}

function closeThread() {
  state.activeThread = "";
  $("dm-thread-screen").classList.add("hidden");
  $("dm-list-screen").classList.remove("hidden");
}

function switchView(view) {
  state.view = view;
  document.querySelectorAll(".tab").forEach((button) => {
    button.classList.toggle("active", button.dataset.view === view);
  });
  document.querySelectorAll(".view").forEach((panel) => {
    panel.classList.toggle("active", panel.id === view + "-view");
  });
}

async function guarded(action) {
  try {
    await action();
    setOnline(true);
  } catch {
    setOnline(false);
  }
}

async function refreshVisibleView() {
  await guarded(refreshPlayerState);
  if (state.view === "feed") await guarded(refreshFeed);
  else if (state.activeThread) await guarded(refreshThread);
  else await guarded(refreshDmList);
}

document.querySelectorAll(".tab").forEach((button) => {
  button.addEventListener("click", () => {
    switchView(button.dataset.view);
    refreshVisibleView();
  });
});

$("dm-back").addEventListener("click", () => {
  closeThread();
  guarded(refreshDmList);
});

$("reply-form").addEventListener("submit", async (event) => {
  event.preventDefault();
  const input = $("reply-text");
  const text = input.value.trim();
  if (!text || !state.activeThread) return;
  input.value = "";
  await guarded(async () => {
    await requestJson("/api/dms/" + encodeURIComponent(state.activeThread) + "/reply", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text }),
    });
    await refreshThread();
  });
});

refreshVisibleView();
setInterval(refreshVisibleView, POLL_MS);
