/* ---------- DOM references --------------------------------------- */
const wrap         = document.getElementById('mapContainer');
const group        = document.getElementById('mapGroup');
const img          = document.getElementById('map');
const dots         = document.getElementById('dots');
const trailsContainer = document.getElementById('trails');
const list         = document.getElementById('playerList');
const btnContainer = document.getElementById('sortButtons');
const tooltip      = document.getElementById('tooltip');
// Chat UI elements
const chatOverlay   = document.getElementById('chatOverlay');
const chatTitle     = document.getElementById('chatTitle');
const chatClose     = document.getElementById('chatClose');
const chatMessages  = document.getElementById('chatMessages');
const chatForm      = document.getElementById('chatForm');
const chatInput     = document.getElementById('chatInput');
let chatSocket      = null;
let currentChatName = null;

/* ---------- constants ------------------------------------------- */
const MAX_Z       = 10;
const FOCUS_ZOOM  = 3;       // zoom level when you click a name
const POLL_MS     = 2000;
const MAP_BOUNDS  = {
  west : -102.04,
  east :  102.19,
  north:  102.16,
  south: -102.00
};

/* ---------- sort configuration ---------------------------------- */
const sortOptions = [
  {
    value: "name",
    label: "Name ↑",
    comparator: (a, b) => a.character_name.localeCompare(b.character_name)
  },
  {
    value: "kph",
    label: "KPH ↓",
    comparator: (a, b) => b.kills_per_hour - a.kills_per_hour
  },
  {
    value: "kills",
    label: "Kills ↓",
    comparator: (a, b) => b.kills - a.kills
  },
  {
    value: "rares",
    label: "Rares ↓",
    comparator: (a, b) => (b.rares_found || 0) - (a.rares_found || 0)
  }
];

let currentSort    = sortOptions[0];
let currentPlayers = [];

/* ---------- generate segmented buttons -------------------------- */
sortOptions.forEach(opt => {
  const btn = document.createElement('div');
  btn.className = 'btn';
  btn.textContent = opt.label;
  btn.dataset.value = opt.value;
  if (opt.value === currentSort.value) btn.classList.add('active');

  btn.addEventListener('click', () => {
    btnContainer.querySelectorAll('.btn')
      .forEach(b => b.classList.remove('active'));
    btn.classList.add('active');
    currentSort = opt;
    renderList();
  });

  btnContainer.appendChild(btn);
});

/* ---------- map & state variables ------------------------------- */
let imgW = 0, imgH = 0;
let scale = 1, offX = 0, offY = 0, minScale = 1;
let dragging = false, sx = 0, sy = 0;
let selected = "";
let pollID = null;

/* ---------- utility functions ----------------------------------- */
const hue = name => {
  let h = 0;
  for (let c of name) h = c.charCodeAt(0) + ((h << 5) - h);
  return `hsl(${Math.abs(h) % 360},72%,50%)`;
};

const loc = (ns, ew) =>
  `${Math.abs(ns).toFixed(1)}${ns>=0?"N":"S"} `
  + `${Math.abs(ew).toFixed(1)}${ew>=0?"E":"W"}`;

function worldToPx(ew, ns) {
  const x = ((ew - MAP_BOUNDS.west)
           / (MAP_BOUNDS.east - MAP_BOUNDS.west)) * imgW;
  const y = ((MAP_BOUNDS.north - ns)
           / (MAP_BOUNDS.north - MAP_BOUNDS.south)) * imgH;
  return { x, y };
}

const applyTransform = () =>
  group.style.transform = `translate(${offX}px,${offY}px) scale(${scale})`;

function clampPan() {
  if (!imgW) return;
  const r = wrap.getBoundingClientRect();
  const vw = r.width, vh = r.height;
  const mw = imgW * scale, mh = imgH * scale;

  offX = mw <= vw ? (vw - mw) / 2 : Math.min(0, Math.max(vw - mw, offX));
  offY = mh <= vh ? (vh - mh) / 2 : Math.min(0, Math.max(vh - mh, offY));
}

function updateView() {
  clampPan();
  applyTransform();
}

function fitToWindow() {
  const r = wrap.getBoundingClientRect();
  scale = Math.min(r.width / imgW, r.height / imgH);
  minScale = scale;
  updateView();
}

/* ---------- tooltip handlers ------------------------------------ */
function showTooltip(evt, p) {
  tooltip.textContent = `${p.character_name} — Kills: ${p.kills} - Kills/h: ${p.kills_per_hour}`;
  const r = wrap.getBoundingClientRect();
  tooltip.style.left    = `${evt.clientX - r.left + 10}px`;
  tooltip.style.top     = `${evt.clientY - r.top  + 10}px`;
  tooltip.style.display = 'block';
}
function hideTooltip() {
  tooltip.style.display = 'none';
}

/* ---------- polling and initialization -------------------------- */
async function pollLive() {
  try {
    const [liveRes, trailsRes] = await Promise.all([
      fetch('/live/'),
      fetch('/trails/?seconds=600'),
    ]);
    const { players } = await liveRes.json();
    const { trails } = await trailsRes.json();
    currentPlayers = players;
    renderTrails(trails);
    renderList();
  } catch (e) {
    console.error('Live or trails fetch failed:', e);
  }
}

function startPolling() {
  if (pollID !== null) return;
  pollLive();
  pollID = setInterval(pollLive, POLL_MS);
}
// -------------------- WebSocket live updates --------------------
let wsLive = null;
function startWebSocket() {
  const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
  const wsUrl = `${protocol}//${location.host}/ws/track`;
  wsLive = new WebSocket(wsUrl);
  wsLive.onopen = () => console.log('WS /ws/track connected');
  wsLive.onmessage = evt => {
    try {
      const snap = JSON.parse(evt.data);
      currentPlayers = currentPlayers.filter(p => p.character_name !== snap.character_name)
                     .concat(snap);
      renderList();
    } catch (e) {
      console.error('Invalid WS message', e);
    }
  };
  wsLive.onclose = () => console.log('WS /ws/track closed');
  wsLive.onerror = e => console.error('WS error', e);
}

img.onload = () => {
  imgW = img.naturalWidth;
  imgH = img.naturalHeight;
  // size the SVG trails container to match the map dimensions
  if (trailsContainer) {
    trailsContainer.setAttribute('viewBox', `0 0 ${imgW} ${imgH}`);
    trailsContainer.setAttribute('width', `${imgW}`);
    trailsContainer.setAttribute('height', `${imgH}`);
  }
  fitToWindow();
  startPolling();
  // start live updates via WebSocket
  startWebSocket();
};

/* ---------- rendering sorted list & dots ------------------------ */
function renderList() {
  const sorted = [...currentPlayers].sort(currentSort.comparator);
  render(sorted);
}

function render(players) {
  dots.innerHTML = '';
  list.innerHTML = '';

  players.forEach(p => {
    const { x, y } = worldToPx(p.ew, p.ns);

    // dot
    const dot = document.createElement('div');
    dot.className = 'dot';
    dot.style.left      = `${x}px`;
    dot.style.top       = `${y}px`;
    dot.style.background = hue(p.character_name);

    


    // custom tooltip
    dot.addEventListener('mouseenter', e => showTooltip(e, p));
    dot.addEventListener('mousemove',  e => showTooltip(e, p));
    dot.addEventListener('mouseleave', hideTooltip);

    // click to select/zoom
    dot.addEventListener('click', () => selectPlayer(p, x, y));

    if (p.character_name === selected) dot.classList.add('highlight');
    dots.appendChild(dot);
   //sidebar
    const li = document.createElement('li');
    const color = hue(p.character_name);
    li.style.borderLeftColor = color;
    li.className = 'player-item';
    li.innerHTML = `
    <span class="player-name">${p.character_name}</span>
    <span class="player-loc">${loc(p.ns, p.ew)}</span>
    <span class="stat kills">${p.kills}</span>
    <span class="stat kph">${p.kills_per_hour}</span>
    <span class="stat rares">${p.rares_found}</span>
    <span class="stat meta">${p.vt_state}</span>
  `;
  
    // Add Chat button
    const chatBtn = document.createElement('button');
    chatBtn.textContent = 'Chat';
    chatBtn.className = 'chat-btn';
    chatBtn.addEventListener('click', e => { e.stopPropagation(); openChat(p.character_name); });
    li.appendChild(chatBtn);
    li.addEventListener('click', () => selectPlayer(p, x, y));
    if (p.character_name === selected) li.classList.add('selected');
    list.appendChild(li);
  });
}

/* ---------- rendering trails ------------------------------- */
function renderTrails(trailData) {
  trailsContainer.innerHTML = '';
  const byChar = trailData.reduce((acc, pt) => {
    (acc[pt.character_name] = acc[pt.character_name] || []).push(pt);
    return acc;
  }, {});
  for (const [name, pts] of Object.entries(byChar)) {
    if (pts.length < 2) continue;
    const points = pts.map(pt => {
      const { x, y } = worldToPx(pt.ew, pt.ns);
      return `${x},${y}`;
    }).join(' ');
    const poly = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
    poly.setAttribute('points', points);
    poly.setAttribute('stroke', hue(name));
    poly.setAttribute('fill', 'none');
    poly.setAttribute('class', 'trail-path');
    trailsContainer.appendChild(poly);
  }
}
/* ---------- selection centering, focus zoom & blink ------------ */
function selectPlayer(p, x, y) {
  selected = p.character_name;
  // set focus zoom
  scale = Math.min(MAX_Z, Math.max(minScale, FOCUS_ZOOM));
  // center on the player
  const r = wrap.getBoundingClientRect();
  offX = r.width  / 2 - x * scale;
  offY = r.height / 2 - y * scale;
  updateView();
  renderList();  // keep sorted + highlight
}

/* ---------- pan & zoom handlers -------------------------------- */
wrap.addEventListener('wheel', e => {
  e.preventDefault();
  if (!imgW) return;

  const r  = wrap.getBoundingClientRect();
  const mx = (e.clientX - r.left - offX) / scale;
  const my = (e.clientY - r.top  - offY) / scale;
  const factor = e.deltaY > 0 ? 0.9 : 1.1;
  let ns = scale * factor;
  ns = Math.max(minScale, Math.min(MAX_Z, ns));

  offX -= mx * (ns - scale);
  offY -= my * (ns - scale);
  scale = ns;
  updateView();
}, { passive: false });

wrap.addEventListener('mousedown', e => {
  dragging = true; sx = e.clientX; sy = e.clientY;
  wrap.classList.add('dragging');
});
window.addEventListener('mousemove', e => {
  if (!dragging) return;
  offX += e.clientX - sx; offY += e.clientY - sy;
  sx = e.clientX; sy = e.clientY;
  updateView();
});
window.addEventListener('mouseup', () => {
  dragging = false; wrap.classList.remove('dragging');
});

wrap.addEventListener('touchstart', e => {
  if (e.touches.length !== 1) return;
  dragging = true;
  sx = e.touches[0].clientX; sy = e.touches[0].clientY;
});
wrap.addEventListener('touchmove', e => {
  if (!dragging || e.touches.length !== 1) return;
  const t = e.touches[0];
  offX += t.clientX - sx; offY += t.clientY - sy;
  sx = t.clientX; sy = t.clientY;
  updateView();
});
wrap.addEventListener('touchend', () => {
  dragging = false;
});
// Chat UI functions
function openChat(name) {
  currentChatName = name;
  chatTitle.textContent = `Chat: ${name}`;
  chatMessages.innerHTML = '';
  chatOverlay.classList.remove('hidden');
  const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
  const wsUrl = `${protocol}//${location.host}/ws/command?character_name=${encodeURIComponent(name)}`;
  chatSocket = new WebSocket(wsUrl);
  chatSocket.onmessage = evt => {
    try {
      const msg = JSON.parse(evt.data);
      appendChatMessage(msg);
    } catch (e) { console.error('Invalid chat msg', e); }
  };
  chatSocket.onopen = () => console.log(`Chat WS connected: ${wsUrl}`);
  chatSocket.onclose = () => console.log('Chat WS closed');
  chatSocket.onerror = e => console.error('Chat WS error', e);
}

function closeChat() {
  if (chatSocket) { chatSocket.close(); chatSocket = null; }
  chatOverlay.classList.add('hidden');
  currentChatName = null;
}

function appendChatMessage(msg) {
  const div = document.createElement('div');
  div.className = 'chat-message';
  const time = new Date(msg.timestamp).toLocaleTimeString();
  const who = msg.from === 'browser' ? 'You' : msg.from;
  div.textContent = `[${time}] ${who}: ${msg.text}`;
  chatMessages.appendChild(div);
  chatMessages.scrollTop = chatMessages.scrollHeight;
}

chatClose.addEventListener('click', closeChat);
chatForm.addEventListener('submit', e => {
  e.preventDefault();
  if (!chatSocket || !currentChatName) return;
  const text = chatInput.value.trim();
  if (!text) return;
  chatSocket.send(JSON.stringify({ command: text }));
  appendChatMessage({ from: 'browser', text, timestamp: new Date().toISOString() });
  chatInput.value = '';
});
