// ===== 게이소개팅 월드 클라이언트 =====

const socket = io();

// 상태
let myPlayer = null;
let players = {};
let buildings = [];
let mapData = null;
let camera = { x: 0, y: 0 };
let keys = {};
let chatOpen = false;
let selectedPlayerId = null;
let currentPrivateRoom = null;
let moveTarget = null; // 클릭 이동 목표 { x, y }
let insideBuilding = null; // 현재 들어가 있는 건물 데이터
let insideBuildingIndex = null;
let buildingPlayers = []; // 건물 안 플레이어 목록

// 캔버스
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// 아바타 팔레트 선택 (pixel-sprites.js의 PALETTES 사용)
const AVATAR_LABELS = ['빨강', '파랑', '주황', '보라', '노랑', '초록', '화이트', '핑크', '블랙', '시안', '오렌지', '네온'];

// ===== 로비 =====
const emojiGrid = document.getElementById('emojiGrid');
let selectedPaletteIdx = 0;

// 팔레트 프리뷰 그리기
function createPalettePreview(paletteIdx) {
  const oc = document.createElement('canvas');
  oc.width = 48;
  oc.height = 72;
  const octx = oc.getContext('2d');
  octx.imageSmoothingEnabled = false;
  const palette = PALETTES[paletteIdx];
  const spriteData = SPRITE_FRONT;
  const rows = spriteData.length;
  const cols = spriteData[0].length;
  const scale = 4;
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      const ch = spriteData[r][c];
      const color = getColorForChar(ch, palette);
      if (color) {
        octx.fillStyle = color;
        octx.fillRect(c * scale, r * scale, scale, scale);
      }
    }
  }
  return oc.toDataURL();
}

PALETTES.forEach((palette, idx) => {
  const el = document.createElement('div');
  el.className = 'emoji-option' + (idx === selectedPaletteIdx ? ' selected' : '');
  const img = document.createElement('img');
  img.src = createPalettePreview(idx);
  img.style.width = '36px';
  img.style.height = '54px';
  img.style.imageRendering = 'pixelated';
  el.appendChild(img);
  el.addEventListener('click', () => {
    document.querySelectorAll('.emoji-option').forEach(e => e.classList.remove('selected'));
    el.classList.add('selected');
    selectedPaletteIdx = idx;
  });
  emojiGrid.appendChild(el);
});

document.getElementById('joinBtn').addEventListener('click', joinGame);
document.getElementById('nicknameInput').addEventListener('keydown', (e) => {
  if (e.key === 'Enter') joinGame();
});

function joinGame() {
  const nickname = document.getElementById('nicknameInput').value.trim() || '익명';
  document.getElementById('lobby').style.display = 'none';
  document.getElementById('game').style.display = 'block';
  resizeCanvas();
  socket.emit('player:join', { nickname, profileEmoji: String(selectedPaletteIdx), paletteIndex: selectedPaletteIdx });
}

// ===== 캔버스 리사이즈 =====
function resizeCanvas() {
  canvas.width = window.innerWidth * window.devicePixelRatio;
  canvas.height = window.innerHeight * window.devicePixelRatio;
  ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
  ctx.imageSmoothingEnabled = false;
}
window.addEventListener('resize', resizeCanvas);

// ===== 소켓 이벤트 =====
socket.on('game:init', (data) => {
  myPlayer = data.player;
  mapData = data.map;
  buildings = data.buildings;
  players = {};
  data.players.forEach(p => { players[p.id] = p; });
});

socket.on('player:joined', (player) => {
  players[player.id] = player;
  addSystemMessage(`${player.nickname}님이 입장했습니다 ✨`);
});

socket.on('player:left', (data) => {
  const p = players[data.id];
  if (p) addSystemMessage(`${p.nickname}님이 퇴장했습니다`);
  delete players[data.id];
});

socket.on('player:moved', (data) => {
  const p = players[data.id];
  if (p) {
    p.x = data.x;
    p.y = data.y;
    p.direction = data.direction;
  }
});

socket.on('player:count', (count) => {
  document.getElementById('playerCount').textContent = `👥 ${count}`;
  document.getElementById('lobbyCount').textContent = count;
});

// 채팅
socket.on('chat:message', (msg) => {
  addChatMessage(msg);
  showBubble(msg.senderId, msg.text, msg.image);
});

socket.on('chat:error', (data) => {
  addSystemMessage(data.message);
});

// 건물 입장/퇴장
socket.on('building:entered', (data) => {
  insideBuilding = data.building;
  insideBuildingIndex = data.index;
  buildingPlayers = data.players;
  document.getElementById('locationLabel').textContent = `📍 ${data.building.emoji} ${data.building.label} (안)`;
});

socket.on('building:left', (data) => {
  myPlayer.x = data.x;
  myPlayer.y = data.y;
  players[socket.id] = myPlayer;
  insideBuilding = null;
  insideBuildingIndex = null;
  buildingPlayers = [];
});

socket.on('building:playerJoined', (data) => {
  buildingPlayers.push(data.player);
  addSystemMessage(`${data.player.nickname}님이 들어왔습니다 ✨`);
});

socket.on('building:playerLeft', (data) => {
  buildingPlayers = buildingPlayers.filter(p => p.id !== data.playerId);
  addSystemMessage(`${data.nickname}님이 나갔습니다`);
});

socket.on('building:chat', (msg) => {
  addChatMessage({ ...msg, isMine: msg.senderId === socket.id });
});

socket.on('player:enteredBuilding', (data) => {
  const p = players[data.id];
  if (p) p.insideBuilding = data.buildingIndex;
});

socket.on('player:leftBuilding', (data) => {
  const p = players[data.id];
  if (p) {
    p.x = data.x;
    p.y = data.y;
    p.insideBuilding = null;
  }
});

// 대화 요청
socket.on('chat:request', (data) => {
  showChatRequest(data);
});

socket.on('chat:started', (data) => {
  currentPrivateRoom = data.roomId;
  const other = data.players.find(p => p.id !== socket.id);
  document.getElementById('privateName').textContent = `💬 ${other?.nickname || '상대방'}`;
  document.getElementById('privateMessages').innerHTML = '';
  document.getElementById('privateChat').style.display = 'flex';
  addPrivateMessage({ nickname: '시스템', text: '1:1 대화가 시작되었습니다! 서로 반갑게 인사해보세요 😊', system: true });
});

socket.on('chat:private', (msg) => {
  addPrivateMessage(msg);
});

socket.on('chat:left', (data) => {
  addPrivateMessage({ nickname: '시스템', text: `${data.nickname}님이 나갔습니다`, system: true });
});

// 리액션
socket.on('reaction:received', (data) => {
  const types = { heart: '❤️', wink: '😉', wave: '👋' };
  showReactionNotify(`${data.nickname}님이 ${types[data.type] || '💕'}를 보냈어요!`);
});

// ===== 채팅 UI =====
const chatBox = document.getElementById('chatBox');
const chatMessages = document.getElementById('chatMessages');
const chatInput = document.getElementById('chatInput');

document.getElementById('chatToggleBtn').addEventListener('click', () => {
  chatOpen = !chatOpen;
  chatBox.classList.toggle('chat-hidden', !chatOpen);
  if (chatOpen) chatInput.focus();
});

document.getElementById('chatSendBtn').addEventListener('click', sendChat);
chatInput.addEventListener('keydown', (e) => {
  if (e.key === 'Enter') sendChat();
  e.stopPropagation();
});

// 이미지 전송 (근접/건물 채팅)
let chatPendingImage = null;
setupImageButton('chatImageBtn', 'chatImageInput', 'chat');
document.getElementById('chatPreviewCancel').addEventListener('click', () => {
  chatPendingImage = null;
  document.getElementById('chatImagePreview').style.display = 'none';
});

function sendChat() {
  const text = chatInput.value.trim();
  const image = chatPendingImage;
  if (!text && !image) return;
  const payload = { text };
  if (image) payload.image = image;
  if (insideBuilding) {
    socket.emit('building:chat', payload);
  } else {
    socket.emit('chat:message', payload);
  }
  chatInput.value = '';
  chatPendingImage = null;
  document.getElementById('chatImagePreview').style.display = 'none';
}

function addChatMessage(msg) {
  const div = document.createElement('div');
  div.className = 'chat-msg' + (msg.isMine ? ' mine' : '');
  let html = `<span class="sender">${escapeHtml(msg.nickname)}</span>`;
  if (msg.text) html += escapeHtml(msg.text);
  if (msg.image) {
    html += `<img class="chat-image" src="${msg.image}" onclick="openImageViewer(this.src)">`;
  }
  div.innerHTML = html;
  chatMessages.appendChild(div);
  chatMessages.scrollTop = chatMessages.scrollHeight;
}

function addSystemMessage(text) {
  const div = document.createElement('div');
  div.className = 'chat-msg system';
  div.textContent = text;
  chatMessages.appendChild(div);
  chatMessages.scrollTop = chatMessages.scrollHeight;
}

// ===== 말풍선 =====
const bubbles = [];

function showBubble(playerId, text, image) {
  const bubble = {
    playerId,
    text: text ? (text.length > 30 ? text.slice(0, 30) + '...' : text) : '',
    time: Date.now(),
    duration: image ? 5000 : 4000,
    image: null,
    imageLoaded: false,
  };
  if (image) {
    const img = new Image();
    img.onload = () => {
      bubble.image = img;
      bubble.imageLoaded = true;
    };
    img.src = image;
  }
  bubbles.push(bubble);
}

// ===== 프로필 팝업 =====
document.getElementById('profileClose').addEventListener('click', closeProfile);

document.getElementById('actionChat').addEventListener('click', () => {
  if (!selectedPlayerId) return;
  socket.emit('chat:request', { targetId: selectedPlayerId });
  closeProfile();
  addSystemMessage('대화를 신청했습니다...');
});

document.getElementById('actionHeart').addEventListener('click', () => {
  if (!selectedPlayerId) return;
  socket.emit('reaction:send', { targetId: selectedPlayerId, type: 'heart' });
  closeProfile();
});

document.getElementById('actionWave').addEventListener('click', () => {
  if (!selectedPlayerId) return;
  socket.emit('reaction:send', { targetId: selectedPlayerId, type: 'wave' });
  closeProfile();
});

function showProfile(player) {
  selectedPlayerId = player.id;
  document.getElementById('profileEmoji').textContent = player.profileEmoji || '😊';
  document.getElementById('profileName').textContent = player.nickname;
  document.getElementById('profilePopup').style.display = 'flex';
}

function closeProfile() {
  document.getElementById('profilePopup').style.display = 'none';
  selectedPlayerId = null;
}

// ===== 대화 요청 =====
let pendingRequest = null;

function showChatRequest(data) {
  pendingRequest = data;
  document.getElementById('requestText').textContent = `${data.emoji} ${data.nickname}님이 대화를 신청했어요!`;
  document.getElementById('chatRequestPopup').style.display = 'block';
}

document.getElementById('requestAccept').addEventListener('click', () => {
  if (pendingRequest) {
    socket.emit('chat:accept', { fromId: pendingRequest.fromId });
  }
  document.getElementById('chatRequestPopup').style.display = 'none';
  pendingRequest = null;
});

document.getElementById('requestDecline').addEventListener('click', () => {
  document.getElementById('chatRequestPopup').style.display = 'none';
  pendingRequest = null;
});

// ===== 1:1 채팅 =====
document.getElementById('privateSendBtn').addEventListener('click', sendPrivate);
document.getElementById('privateInput').addEventListener('keydown', (e) => {
  if (e.key === 'Enter') sendPrivate();
  e.stopPropagation();
});

// 이미지 전송 (1:1)
let privatePendingImage = null;
setupImageButton('privateImageBtn', 'privateImageInput', 'private');
document.getElementById('privatePreviewCancel').addEventListener('click', () => {
  privatePendingImage = null;
  document.getElementById('privateImagePreview').style.display = 'none';
});

document.getElementById('privateLeave').addEventListener('click', () => {
  if (currentPrivateRoom) {
    socket.emit('chat:leave', { roomId: currentPrivateRoom });
    currentPrivateRoom = null;
  }
  document.getElementById('privateChat').style.display = 'none';
});

function sendPrivate() {
  const input = document.getElementById('privateInput');
  const text = input.value.trim();
  const image = privatePendingImage;
  if ((!text && !image) || !currentPrivateRoom) return;
  const payload = { roomId: currentPrivateRoom, text };
  if (image) payload.image = image;
  socket.emit('chat:private', payload);
  input.value = '';
  privatePendingImage = null;
  document.getElementById('privateImagePreview').style.display = 'none';
}

function addPrivateMessage(msg) {
  const div = document.createElement('div');
  div.className = 'chat-msg' + (msg.senderId === socket.id ? ' mine' : '') + (msg.system ? ' system' : '');
  if (msg.system) {
    div.textContent = msg.text;
  } else {
    let html = `<span class="sender">${escapeHtml(msg.nickname)}</span>`;
    if (msg.text) html += escapeHtml(msg.text);
    if (msg.image) {
      html += `<img class="chat-image" src="${msg.image}" onclick="openImageViewer(this.src)">`;
    }
    div.innerHTML = html;
  }
  const container = document.getElementById('privateMessages');
  container.appendChild(div);
  container.scrollTop = container.scrollHeight;
}

// ===== 리액션 알림 =====
function showReactionNotify(text) {
  const el = document.getElementById('reactionNotify');
  el.textContent = text;
  el.style.display = 'block';
  setTimeout(() => { el.style.display = 'none'; }, 3000);
}

// ===== 키보드 입력 =====
document.addEventListener('keydown', (e) => {
  keys[e.key] = true;
  // 채팅 토글
  if (e.key === 'Enter' && document.activeElement !== chatInput && document.activeElement !== document.getElementById('privateInput')) {
    chatOpen = !chatOpen;
    chatBox.classList.toggle('chat-hidden', !chatOpen);
    if (chatOpen) setTimeout(() => chatInput.focus(), 50);
  }
});

document.addEventListener('keyup', (e) => {
  keys[e.key] = false;
});

// ===== 모바일 조이스틱 =====
const joystickDirs = {};

document.querySelectorAll('.joy-btn').forEach((btn) => {
  const dir = btn.dataset.dir;

  const start = () => { joystickDirs[dir] = true; };
  const end = () => { joystickDirs[dir] = false; };

  btn.addEventListener('touchstart', (e) => { e.preventDefault(); start(); });
  btn.addEventListener('touchend', (e) => { e.preventDefault(); end(); });
  btn.addEventListener('touchcancel', (e) => { e.preventDefault(); end(); });
  btn.addEventListener('mousedown', start);
  btn.addEventListener('mouseup', end);
  btn.addEventListener('mouseleave', end);
});

// ===== 캔버스 클릭으로 플레이어 선택 / 이동 / 건물 입장 =====
canvas.addEventListener('click', (e) => {
  if (!myPlayer || !mapData) return;

  // 건물 내부 모드일 때
  if (insideBuilding) {
    const rect = canvas.getBoundingClientRect();
    const cx = e.clientX - rect.left;
    const cy = e.clientY - rect.top;
    const w = window.innerWidth;
    const h = window.innerHeight;

    // 나가기 버튼 클릭
    if (cx > w / 2 - 60 && cx < w / 2 + 60 && cy > h - 78 && cy < h - 42) {
      socket.emit('building:leave');
      return;
    }

    // 건물 안 플레이어 클릭 → 프로필
    const spacing = Math.min(120, (w - 100) / Math.max(buildingPlayers.length, 1));
    const startX = w / 2 - (buildingPlayers.length - 1) * spacing / 2;
    for (let i = 0; i < buildingPlayers.length; i++) {
      const p = buildingPlayers[i];
      if (p.id === socket.id) continue;
      const px = startX + i * spacing;
      const py = h / 2 + 30;
      if (Math.sqrt((cx - px) ** 2 + (cy - py) ** 2) < 35) {
        showProfile(p);
        return;
      }
    }
    return;
  }

  const rect = canvas.getBoundingClientRect();
  const clickX = (e.clientX - rect.left) + camera.x;
  const clickY = (e.clientY - rect.top) + camera.y;

  // 다른 플레이어 클릭 체크
  for (const id of Object.keys(players)) {
    if (id === socket.id) continue;
    const p = players[id];
    if (p.insideBuilding !== null && p.insideBuilding !== undefined) continue;
    const dist = Math.sqrt((clickX - p.x) ** 2 + (clickY - p.y) ** 2);
    if (dist < 30) {
      showProfile(p);
      return;
    }
  }

  // 건물 클릭 체크 → 문 앞으로 이동 후 입장
  for (let i = 0; i < buildings.length; i++) {
    const b = buildings[i];
    if (clickX > b.x && clickX < b.x + b.w && clickY > b.y && clickY < b.y + b.h) {
      // 건물 문 앞 위치로 이동 목표 설정
      const doorX = b.x + b.w / 2;
      const doorY = b.y + b.h + 30;
      moveTarget = { x: doorX, y: doorY, enterBuilding: i };
      return;
    }
  }

  // 빈 곳 클릭 → 해당 위치로 이동
  if (!checkCollision(clickX, clickY)) {
    moveTarget = {
      x: Math.max(16, Math.min(mapData.width - 16, clickX)),
      y: Math.max(16, Math.min(mapData.height - 16, clickY)),
    };
  }
});

// ===== 리액션 버튼 =====
document.getElementById('reactionBtn').addEventListener('click', () => {
  // 가장 가까운 플레이어에게 하트
  if (!myPlayer) return;
  let closest = null;
  let closestDist = Infinity;
  for (const id of Object.keys(players)) {
    if (id === socket.id) continue;
    const p = players[id];
    const d = Math.sqrt((myPlayer.x - p.x) ** 2 + (myPlayer.y - p.y) ** 2);
    if (d < closestDist && d < 200) {
      closestDist = d;
      closest = p;
    }
  }
  if (closest) {
    socket.emit('reaction:send', { targetId: closest.id, type: 'heart' });
    showReactionNotify(`${closest.nickname}님에게 ❤️를 보냈어요!`);
  }
});

// ===== 유틸 =====
function escapeHtml(str) {
  const div = document.createElement('div');
  div.textContent = str;
  return div.innerHTML;
}

// ===== 이미지 처리 =====

// 모바일 호환 이미지 버튼 설정
// iOS Safari는 비동기 콜백에서 .click()을 차단하므로
// label+for 패턴으로 네이티브 연결하고, 직접 click도 백업으로 건다
function setupImageButton(btnId, inputId, target) {
  const btn = document.getElementById(btnId);
  const input = document.getElementById(inputId);

  // 방법 1: 버튼 클릭 시 input 트리거 (데스크탑 + 일부 모바일)
  btn.addEventListener('click', (e) => {
    e.preventDefault();
    e.stopPropagation();
    input.click();
  });

  // 방법 2: touchend에서도 트리거 (iOS Safari 대응)
  btn.addEventListener('touchend', (e) => {
    e.preventDefault();
    e.stopPropagation();
    input.click();
  });

  // change 이벤트
  input.addEventListener('change', (ev) => {
    handleImageSelect(ev, target);
  });
}

function handleImageSelect(e, target) {
  const file = e.target.files[0];
  if (!file) return;
  if (!file.type.startsWith('image/')) return;

  // 리사이즈 (최대 800px, 품질 0.7)
  resizeImage(file, 800, 0.7, (dataUrl) => {
    if (target === 'chat') {
      chatPendingImage = dataUrl;
      document.getElementById('chatPreviewImg').src = dataUrl;
      document.getElementById('chatImagePreview').style.display = 'block';
    } else {
      privatePendingImage = dataUrl;
      document.getElementById('privatePreviewImg').src = dataUrl;
      document.getElementById('privateImagePreview').style.display = 'block';
    }
  });
  e.target.value = '';
}

function resizeImage(file, maxSize, quality, callback) {
  const reader = new FileReader();
  reader.onload = (e) => {
    const img = new Image();
    img.onload = () => {
      let w = img.width;
      let h = img.height;
      if (w > maxSize || h > maxSize) {
        if (w > h) {
          h = Math.round(h * maxSize / w);
          w = maxSize;
        } else {
          w = Math.round(w * maxSize / h);
          h = maxSize;
        }
      }
      const canvas = document.createElement('canvas');
      canvas.width = w;
      canvas.height = h;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(img, 0, 0, w, h);
      callback(canvas.toDataURL('image/jpeg', quality));
    };
    img.src = e.target.result;
  };
  reader.readAsDataURL(file);
}

function openImageViewer(src) {
  document.getElementById('viewerImg').src = src;
  document.getElementById('imageViewer').style.display = 'flex';
}

// 파일을 현재 활성 채팅으로 세팅
function setImageFromFile(file) {
  if (!file || !file.type.startsWith('image/')) return;
  const target = document.getElementById('privateChat').style.display !== 'none' ? 'private' : 'chat';
  // 채팅이 닫혀있으면 열기
  if (target === 'chat' && !chatOpen) {
    chatOpen = true;
    chatBox.classList.remove('chat-hidden');
  }
  resizeImage(file, 800, 0.7, (dataUrl) => {
    if (target === 'chat') {
      chatPendingImage = dataUrl;
      document.getElementById('chatPreviewImg').src = dataUrl;
      document.getElementById('chatImagePreview').style.display = 'block';
    } else {
      privatePendingImage = dataUrl;
      document.getElementById('privatePreviewImg').src = dataUrl;
      document.getElementById('privateImagePreview').style.display = 'block';
    }
  });
}

// ===== 드래그 앤 드롭 이미지 업로드 =====
document.addEventListener('dragover', (e) => {
  e.preventDefault();
  document.getElementById('game').classList.add('drag-over');
});

document.addEventListener('dragleave', (e) => {
  if (e.relatedTarget === null || !document.getElementById('game').contains(e.relatedTarget)) {
    document.getElementById('game').classList.remove('drag-over');
  }
});

document.addEventListener('drop', (e) => {
  e.preventDefault();
  document.getElementById('game').classList.remove('drag-over');
  const file = e.dataTransfer.files[0];
  if (file) setImageFromFile(file);
});

// 클립보드 붙여넣기 (Ctrl+V)
document.addEventListener('paste', (e) => {
  const items = e.clipboardData?.items;
  if (!items) return;
  for (const item of items) {
    if (item.type.startsWith('image/')) {
      e.preventDefault();
      setImageFromFile(item.getAsFile());
      return;
    }
  }
});

// ===== 충돌 체크 =====
function checkCollision(x, y) {
  for (const b of buildings) {
    if (b.isPlaza) continue; // 광장은 통과 가능
    if (x > b.x && x < b.x + b.w && y > b.y && y < b.y + b.h) {
      return true;
    }
  }
  return false;
}

// ===== 현재 위치 라벨 =====
function updateLocationLabel() {
  if (!myPlayer) return;
  let label = '거리';
  for (const b of buildings) {
    const cx = b.x + b.w / 2;
    const cy = b.y + b.h / 2;
    const dist = Math.sqrt((myPlayer.x - cx) ** 2 + (myPlayer.y - cy) ** 2);
    if (dist < 150) {
      label = b.label;
      break;
    }
  }
  document.getElementById('locationLabel').textContent = `📍 ${label}`;
}

// ===== 게임 루프 =====
const SPEED = 3;
let lastMoveEmit = 0;

function gameLoop() {
  if (!myPlayer || !mapData) {
    requestAnimationFrame(gameLoop);
    return;
  }

  // 이동
  let dx = 0, dy = 0;
  if (keys['w'] || keys['W'] || keys['ArrowUp'] || joystickDirs.up) { dy = -SPEED; moveTarget = null; }
  if (keys['s'] || keys['S'] || keys['ArrowDown'] || joystickDirs.down) { dy = SPEED; moveTarget = null; }
  if (keys['a'] || keys['A'] || keys['ArrowLeft'] || joystickDirs.left) { dx = -SPEED; moveTarget = null; }
  if (keys['d'] || keys['D'] || keys['ArrowRight'] || joystickDirs.right) { dx = SPEED; moveTarget = null; }

  // 건물 안에 있으면 월드 이동 스킵
  if (insideBuilding) {
    render();
    requestAnimationFrame(gameLoop);
    return;
  }

  // 클릭 이동
  if (moveTarget && dx === 0 && dy === 0) {
    const tdx = moveTarget.x - myPlayer.x;
    const tdy = moveTarget.y - myPlayer.y;
    const dist = Math.sqrt(tdx * tdx + tdy * tdy);
    if (dist < SPEED + 1) {
      // 건물 입장 목표였으면 입장
      if (moveTarget.enterBuilding !== undefined) {
        socket.emit('building:enter', { index: moveTarget.enterBuilding });
      }
      moveTarget = null;
    } else {
      dx = (tdx / dist) * SPEED;
      dy = (tdy / dist) * SPEED;
    }
  }

  // 대각선 보정 (키보드 입력만)
  if (!moveTarget && dx !== 0 && dy !== 0) {
    dx *= 0.707;
    dy *= 0.707;
  }

  if (dx !== 0 || dy !== 0) {
    const newX = Math.max(16, Math.min(mapData.width - 16, myPlayer.x + dx));
    const newY = Math.max(16, Math.min(mapData.height - 16, myPlayer.y + dy));

    if (!checkCollision(newX, newY)) {
      myPlayer.x = newX;
      myPlayer.y = newY;
    }

    // 방향
    if (Math.abs(dx) > Math.abs(dy)) {
      myPlayer.direction = dx > 0 ? 'right' : 'left';
    } else {
      myPlayer.direction = dy > 0 ? 'down' : 'up';
    }

    // 서버 전송 (쓰로틀링)
    const now = Date.now();
    if (now - lastMoveEmit > 50) {
      socket.emit('player:move', { x: myPlayer.x, y: myPlayer.y, direction: myPlayer.direction });
      lastMoveEmit = now;
    }

    players[socket.id] = myPlayer;
  }

  updateLocationLabel();

  // 카메라
  const screenW = window.innerWidth;
  const screenH = window.innerHeight;
  camera.x = myPlayer.x - screenW / 2;
  camera.y = myPlayer.y - screenH / 2;
  camera.x = Math.max(0, Math.min(mapData.width - screenW, camera.x));
  camera.y = Math.max(0, Math.min(mapData.height - screenH, camera.y));

  // 렌더링
  render();

  requestAnimationFrame(gameLoop);
}

// ===== 렌더링 =====
function render() {
  const w = window.innerWidth;
  const h = window.innerHeight;

  ctx.clearRect(0, 0, w, h);

  // 건물 내부 모드
  if (insideBuilding) {
    renderBuildingInterior(w, h);
    return;
  }

  ctx.save();
  ctx.translate(-camera.x, -camera.y);

  // 바닥 (잔디 + 도로)
  drawMap();

  // 건물
  drawBuildings();

  // 플레이어들
  drawPlayers();

  // 클릭 이동 목표 표시
  if (moveTarget) {
    const pulse = Math.sin(Date.now() / 200) * 4 + 8;
    ctx.strokeStyle = 'rgba(240,147,251,0.6)';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.arc(moveTarget.x, moveTarget.y, pulse, 0, Math.PI * 2);
    ctx.stroke();
    ctx.fillStyle = 'rgba(240,147,251,0.3)';
    ctx.beginPath();
    ctx.arc(moveTarget.x, moveTarget.y, 4, 0, Math.PI * 2);
    ctx.fill();
  }

  // 말풍선
  drawBubbles();

  ctx.restore();
}

function renderBuildingInterior(w, h) {
  const b = insideBuilding;

  // 배경 (레트로 스타일)
  ctx.fillStyle = b.interior || '#2d2d44';
  ctx.fillRect(0, 0, w, h);

  // 체크 바닥 패턴
  const tileSize = 32;
  for (let x = 0; x < w; x += tileSize) {
    for (let y = h * 0.6; y < h; y += tileSize) {
      ctx.fillStyle = (Math.floor(x / tileSize) + Math.floor(y / tileSize)) % 2 === 0
        ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';
      ctx.fillRect(x, y, tileSize, tileSize);
    }
  }

  // 뒷벽
  ctx.fillStyle = 'rgba(255,255,255,0.05)';
  ctx.fillRect(0, 0, w, h * 0.35);
  // 벽 경계선
  ctx.fillStyle = 'rgba(255,255,255,0.15)';
  ctx.fillRect(0, h * 0.35 - 2, w, 4);

  // 건물 이름 (픽셀 폰트 스타일)
  ctx.font = 'bold 24px monospace';
  ctx.textAlign = 'center';
  ctx.fillStyle = '#fff';
  ctx.fillText(`${b.emoji} ${b.label}`, w / 2, 36);

  // 건물 안 플레이어들 (픽셀 캐릭터)
  const allInside = buildingPlayers;
  const spacing = Math.min(100, (w - 100) / Math.max(allInside.length, 1));
  const startX = w / 2 - (allInside.length - 1) * spacing / 2;

  allInside.forEach((p, i) => {
    const px = startX + i * spacing;
    const py = h / 2 + 20;
    const isMe = p.id === socket.id;

    drawPixelCharacterLarge(ctx, px, py, p, 2.5);

    // 닉네임
    ctx.font = 'bold 12px monospace';
    const nameW = ctx.measureText(p.nickname).width + 10;
    ctx.fillStyle = isMe ? '#e74c3c' : 'rgba(0,0,0,0.7)';
    ctx.fillRect(px - nameW / 2, py - 52, nameW, 16);
    ctx.fillStyle = '#fff';
    ctx.textAlign = 'center';
    ctx.fillText(p.nickname, px, py - 40);

    // 근접 표시
    if (!isMe) {
      const pulse = Math.sin(Date.now() / 400 + i) * 0.3 + 0.5;
      ctx.strokeStyle = `rgba(255,80,80,${pulse})`;
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.arc(px, py, 36, 0, Math.PI * 2);
      ctx.stroke();
    }
  });

  // 나가기 버튼 (픽셀 스타일)
  const exitBtnX = w / 2;
  const exitBtnY = h - 60;
  ctx.fillStyle = '#5a3d2b';
  ctx.fillRect(exitBtnX - 50, exitBtnY - 16, 100, 32);
  ctx.fillStyle = '#8B5E3C';
  ctx.fillRect(exitBtnX - 48, exitBtnY - 14, 96, 28);
  ctx.font = 'bold 13px monospace';
  ctx.fillStyle = '#fff';
  ctx.textAlign = 'center';
  ctx.fillText('[ 나가기 ]', exitBtnX, exitBtnY + 5);
  ctx.textAlign = 'left';
}

function drawMap() {
  drawPixelGround(ctx, mapData);

  // 나무 (블록 빈 공간에 배치)
  const treePositions = [
    // row0 빈 공간
    [60, 80], [330, 50], [60, 200],
    [1180, 80], [1180, 200],
    // row1 빈 공간
    [60, 430], [340, 430], [60, 540],
    [1180, 430], [1180, 540],
    // row2 빈 공간
    [60, 750], [340, 750], [60, 860],
    [1180, 750], [1180, 860],
    // 맵 하단 (도로 아래)
    [100, 1050], [500, 1050], [900, 1050], [1200, 1050],
  ];
  treePositions.forEach(([tx, ty]) => drawPixelTree(ctx, tx, ty));

  // 가로등 (도로변 보도블럭 위)
  const lampPositions = [
    [420, 290], [420, 610], [420, 930],
    [860, 290], [860, 610], [860, 930],
    [480, 290], [480, 610],
    [800, 290], [800, 610],
  ];
  lampPositions.forEach(([lx, ly]) => drawPixelLamp(ctx, lx, ly));
}

function drawBuildings() {
  buildings.forEach((b, idx) => {
    drawPixelBuilding(ctx, b, idx, players);
  });
}

// 이동 상태 추적 (걷기 애니메이션용)
const playerMoving = {};
const playerLastPos = {};

function drawPlayers() {
  const sortedPlayers = Object.values(players).sort((a, b) => a.y - b.y);

  sortedPlayers.forEach((p) => {
    if (p.insideBuilding !== null && p.insideBuilding !== undefined) return;
    const isMe = p.id === socket.id;
    const x = p.x;
    const y = p.y;

    // 이동 감지
    const lastPos = playerLastPos[p.id];
    const isMoving = lastPos && (Math.abs(lastPos.x - x) > 0.5 || Math.abs(lastPos.y - y) > 0.5);
    playerLastPos[p.id] = { x, y };

    // 근접 범위 (내 캐릭터만)
    if (isMe) {
      ctx.strokeStyle = 'rgba(255,80,80,0.1)';
      ctx.lineWidth = 1;
      ctx.setLineDash([4, 4]);
      ctx.beginPath();
      ctx.arc(x, y, 200, 0, Math.PI * 2);
      ctx.stroke();
      ctx.setLineDash([]);
    }

    // 픽셀 캐릭터 그리기
    drawPixelCharacter(ctx, x, y, p, isMoving);

    // 닉네임 (픽셀 스타일)
    ctx.font = 'bold 10px monospace';
    ctx.textAlign = 'center';
    const nameWidth = ctx.measureText(p.nickname).width + 8;
    ctx.fillStyle = isMe ? '#c0392b' : 'rgba(0,0,0,0.7)';
    ctx.fillRect(x - nameWidth / 2, y - 28, nameWidth, 13);
    ctx.fillStyle = '#fff';
    ctx.fillText(p.nickname, x, y - 18);
    ctx.textAlign = 'left';

    // 근접 반짝임
    if (!isMe && myPlayer) {
      const dist = Math.sqrt((myPlayer.x - x) ** 2 + (myPlayer.y - y) ** 2);
      if (dist < 100) {
        const pulse = Math.sin(Date.now() / 300) * 0.4 + 0.6;
        ctx.strokeStyle = `rgba(255,80,80,${pulse})`;
        ctx.lineWidth = 2;
        ctx.setLineDash([3, 3]);
        ctx.beginPath();
        ctx.arc(x, y, 22, 0, Math.PI * 2);
        ctx.stroke();
        ctx.setLineDash([]);
      }
    }
  });
}

function drawBubbles() {
  const now = Date.now();
  for (let i = bubbles.length - 1; i >= 0; i--) {
    const b = bubbles[i];
    const elapsed = now - b.time;
    if (elapsed > b.duration) {
      bubbles.splice(i, 1);
      continue;
    }

    const p = players[b.playerId];
    if (!p) continue;

    const alpha = elapsed < 300 ? elapsed / 300 :
                  elapsed > b.duration - 500 ? (b.duration - elapsed) / 500 : 1;

    ctx.globalAlpha = alpha;

    const hasImage = b.imageLoaded && b.image;
    const hasText = b.text && b.text.length > 0;

    // 이미지 썸네일 크기
    const imgW = 60, imgH = 60;

    // 말풍선 크기 계산
    ctx.font = '11px monospace';
    const textW = hasText ? Math.min(ctx.measureText(b.text).width + 12, 160) : 0;
    const bubbleW = Math.max(hasImage ? imgW + 8 : 0, textW, 40);
    const bubbleH = (hasImage ? imgH + 6 : 0) + (hasText ? 18 : 0) + 4;

    const bx = p.x - bubbleW / 2;
    const by = p.y - 50 - bubbleH + 20;

    // 흰 배경 + 검정 테두리
    ctx.fillStyle = '#fff';
    ctx.fillRect(bx, by, bubbleW, bubbleH);
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 2;
    ctx.strokeRect(bx, by, bubbleW, bubbleH);

    // 꼬리
    ctx.fillStyle = '#fff';
    ctx.beginPath();
    ctx.moveTo(p.x - 4, by + bubbleH);
    ctx.lineTo(p.x, by + bubbleH + 6);
    ctx.lineTo(p.x + 4, by + bubbleH);
    ctx.fill();
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(p.x - 4, by + bubbleH);
    ctx.lineTo(p.x, by + bubbleH + 6);
    ctx.lineTo(p.x + 4, by + bubbleH);
    ctx.stroke();
    // 꼬리 안쪽 흰색으로 덮기
    ctx.fillStyle = '#fff';
    ctx.fillRect(p.x - 5, by + bubbleH - 2, 10, 3);

    let contentY = by + 3;

    // 이미지
    if (hasImage) {
      const iw = b.image.naturalWidth || b.image.width;
      const ih = b.image.naturalHeight || b.image.height;
      const scale = Math.min(imgW / iw, imgH / ih);
      const dw = iw * scale;
      const dh = ih * scale;
      const ix = p.x - dw / 2;
      ctx.drawImage(b.image, ix, contentY, dw, dh);
      contentY += imgH + 2;
    }

    // 텍스트
    if (hasText) {
      ctx.fillStyle = '#333';
      ctx.textAlign = 'center';
      ctx.fillText(b.text, p.x, contentY + 12);
      ctx.textAlign = 'left';
    }

    ctx.globalAlpha = 1;
  }
}

// roundRect 폴리필
if (!CanvasRenderingContext2D.prototype.roundRect) {
  CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
    if (typeof r === 'number') r = [r, r, r, r];
    this.moveTo(x + r[0], y);
    this.lineTo(x + w - r[1], y);
    this.quadraticCurveTo(x + w, y, x + w, y + r[1]);
    this.lineTo(x + w, y + h - r[2]);
    this.quadraticCurveTo(x + w, y + h, x + w - r[2], y + h);
    this.lineTo(x + r[3], y + h);
    this.quadraticCurveTo(x, y + h, x, y + h - r[3]);
    this.lineTo(x, y + r[0]);
    this.quadraticCurveTo(x, y, x + r[0], y);
    this.closePath();
  };
}

// ===== 시작 =====
requestAnimationFrame(gameLoop);
