const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, { maxHttpBufferSize: 5e6 }); // 5MB for image uploads

app.use(express.static('public'));

// 게임 상태
const players = new Map();

// 맵 설정
const MAP = {
  width: 1280,
  height: 1100,
  tileSize: 64,
};

// 건물 배치 (충돌 영역)
// 도로: 가로 y=320,640,960 (폭 88), 세로 x=448,832 (폭 88)
// 블록: row0 y=[20,256], row1 y=[384,576], row2 y=[704,896]
//        col0 x=[20,384], col1 x=[512,768], col2 x=[896,1200]
const buildings = [
  { x: 127, y: 73,  w: 150, h: 130, label: '게이빈',   emoji: '☕', interior: '#3d2b1f' },
  { x: 565, y: 73,  w: 150, h: 130, label: '바',       emoji: '🍸', interior: '#1a1a3e' },
  { x: 975, y: 73,  w: 150, h: 130, label: '서점',     emoji: '📚', interior: '#2e1f14' },
  { x: 127, y: 415, w: 150, h: 130, label: '공원 벤치', emoji: '🌳', interior: '#1a3d1a' },
  { x: 540, y: 400, w: 200, h: 170, label: '광장',     emoji: '⛲', interior: '#2d2d44', isPlaza: true },
  { x: 975, y: 415, w: 150, h: 130, label: '영화관',   emoji: '🎬', interior: '#1a1a2e' },
  { x: 127, y: 735, w: 150, h: 130, label: '레스토랑', emoji: '🍽️', interior: '#3d1a1a' },
  { x: 565, y: 735, w: 150, h: 130, label: '꽃집',     emoji: '💐', interior: '#3d2d3d' },
  { x: 975, y: 735, w: 150, h: 130, label: '누누모텔', emoji: '🏩', interior: '#2d1a3d' },
];

// 근접 채팅 거리
const CHAT_DISTANCE = 200;
const WHISPER_DISTANCE = 100;

function getDistance(a, b) {
  return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2);
}

function getNearbyPlayers(playerId) {
  const player = players.get(playerId);
  if (!player) return [];
  const nearby = [];
  for (const [id, other] of players) {
    if (id === playerId) continue;
    if (getDistance(player, other) <= CHAT_DISTANCE) {
      nearby.push(id);
    }
  }
  return nearby;
}

function isInsideBuilding(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 getRandomSpawn() {
  // 도로 교차점 및 안전한 도로 위 위치
  const safeSpots = [
    { x: 448, y: 320 },  // 교차로 1
    { x: 832, y: 320 },  // 교차로 2
    { x: 448, y: 640 },  // 교차로 3
    { x: 832, y: 640 },  // 교차로 4
    { x: 448, y: 960 },  // 교차로 5
    { x: 832, y: 960 },  // 교차로 6
    { x: 640, y: 320 },  // 가로 도로 위
    { x: 640, y: 640 },  // 가로 도로 위
    { x: 700, y: 540 },  // 광장 근처
  ];
  // 약간의 랜덤 오프셋 추가
  const spot = safeSpots[Math.floor(Math.random() * safeSpots.length)];
  const offset = () => (Math.random() - 0.5) * 30;
  return { x: spot.x + offset(), y: spot.y + offset() };
}

// 아바타 색상 팔레트 (레인보우 테마)
const AVATAR_COLORS = [
  0xE74C3C, 0xE67E22, 0xF1C40F, 0x2ECC71,
  0x3498DB, 0x9B59B6, 0xE91E63, 0x00BCD4,
  0xFF6B6B, 0xA29BFE, 0xFD79A8, 0x6C5CE7,
];

io.on('connection', (socket) => {
  console.log(`접속: ${socket.id}`);

  socket.on('player:join', (data) => {
    const spawn = getRandomSpawn();
    const player = {
      id: socket.id,
      nickname: data.nickname || '익명',
      profileEmoji: data.profileEmoji || '0',
      paletteIndex: typeof data.paletteIndex === 'number' ? data.paletteIndex : Math.floor(Math.random() * 12),
      color: typeof data.paletteIndex === 'number' ? data.paletteIndex : Math.floor(Math.random() * 12),
      x: spawn.x,
      y: spawn.y,
      direction: 'down',
      insideBuilding: null, // 건물 인덱스 (null이면 밖)
    };
    players.set(socket.id, player);

    // 본인에게 초기 데이터 전송
    socket.emit('game:init', {
      player,
      players: Array.from(players.values()),
      map: MAP,
      buildings,
    });

    // 다른 플레이어에게 알림
    socket.broadcast.emit('player:joined', player);
    io.emit('player:count', players.size);
  });

  socket.on('player:move', (data) => {
    const player = players.get(socket.id);
    if (!player) return;

    // 서버 검증: 맵 범위 체크
    const x = Math.max(16, Math.min(MAP.width - 16, data.x));
    const y = Math.max(16, Math.min(MAP.height - 16, data.y));

    player.x = x;
    player.y = y;
    player.direction = data.direction || player.direction;

    socket.broadcast.emit('player:moved', {
      id: socket.id,
      x: player.x,
      y: player.y,
      direction: player.direction,
    });
  });

  socket.on('chat:message', (data) => {
    const player = players.get(socket.id);
    if (!player) return;

    // 이미지 검증 (base64 data URL, 최대 4MB)
    let image = null;
    if (data.image && typeof data.image === 'string' && data.image.startsWith('data:image/') && data.image.length < 4 * 1024 * 1024) {
      image = data.image;
    }

    const msg = {
      id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
      senderId: socket.id,
      nickname: player.nickname,
      text: (data.text || '').slice(0, 200),
      image,
      emoji: player.profileEmoji,
      timestamp: Date.now(),
    };

    // 근처 플레이어에게만 전송
    const nearby = getNearbyPlayers(socket.id);
    socket.emit('chat:message', { ...msg, isMine: true });
    nearby.forEach((id) => {
      io.to(id).emit('chat:message', msg);
    });
  });

  // 1:1 대화 요청
  socket.on('chat:request', (data) => {
    const player = players.get(socket.id);
    const target = players.get(data.targetId);
    if (!player || !target) return;
    if (getDistance(player, target) > WHISPER_DISTANCE) {
      socket.emit('chat:error', { message: '더 가까이 가야 대화를 신청할 수 있어요!' });
      return;
    }
    io.to(data.targetId).emit('chat:request', {
      fromId: socket.id,
      nickname: player.nickname,
      emoji: player.profileEmoji,
    });
  });

  socket.on('chat:accept', (data) => {
    const roomId = [socket.id, data.fromId].sort().join('-');
    socket.join(roomId);
    const fromSocket = io.sockets.sockets.get(data.fromId);
    if (fromSocket) fromSocket.join(roomId);

    const player = players.get(socket.id);
    io.to(roomId).emit('chat:started', {
      roomId,
      players: [
        players.get(socket.id),
        players.get(data.fromId),
      ],
    });
  });

  socket.on('chat:private', (data) => {
    const player = players.get(socket.id);
    if (!player || !data.roomId) return;
    let image = null;
    if (data.image && typeof data.image === 'string' && data.image.startsWith('data:image/') && data.image.length < 4 * 1024 * 1024) {
      image = data.image;
    }
    io.to(data.roomId).emit('chat:private', {
      senderId: socket.id,
      nickname: player.nickname,
      emoji: player.profileEmoji,
      text: (data.text || '').slice(0, 200),
      image,
      timestamp: Date.now(),
    });
  });

  socket.on('chat:leave', (data) => {
    if (data.roomId) {
      socket.leave(data.roomId);
      const player = players.get(socket.id);
      io.to(data.roomId).emit('chat:left', {
        playerId: socket.id,
        nickname: player?.nickname,
      });
    }
  });

  // 건물 입장
  socket.on('building:enter', (data) => {
    const player = players.get(socket.id);
    if (!player || typeof data.index !== 'number') return;
    const building = buildings[data.index];
    if (!building) return;

    player.insideBuilding = data.index;
    const roomName = `building-${data.index}`;
    socket.join(roomName);

    // 건물 안에 있는 플레이어 목록
    const insidePlayers = [];
    for (const [id, p] of players) {
      if (p.insideBuilding === data.index) insidePlayers.push(p);
    }

    socket.emit('building:entered', {
      index: data.index,
      building,
      players: insidePlayers,
    });

    // 건물 안 다른 사람들에게 알림
    socket.to(roomName).emit('building:playerJoined', {
      player: { id: player.id, nickname: player.nickname, profileEmoji: player.profileEmoji, color: player.color },
    });

    // 밖에 있는 사람들에게 사라짐 알림
    socket.broadcast.emit('player:enteredBuilding', { id: socket.id, buildingIndex: data.index });
  });

  // 건물 퇴장
  socket.on('building:leave', () => {
    const player = players.get(socket.id);
    if (!player || player.insideBuilding === null) return;

    const buildingIndex = player.insideBuilding;
    const building = buildings[buildingIndex];
    const roomName = `building-${buildingIndex}`;

    // 건물 문 앞에 위치시키기
    player.x = building.x + building.w / 2;
    player.y = building.y + building.h + 30;
    player.insideBuilding = null;

    socket.leave(roomName);
    socket.to(roomName).emit('building:playerLeft', {
      playerId: socket.id,
      nickname: player.nickname,
    });

    socket.emit('building:left', {
      x: player.x,
      y: player.y,
    });

    // 밖에 있는 사람들에게 다시 나타남 알림
    socket.broadcast.emit('player:leftBuilding', {
      id: socket.id,
      x: player.x,
      y: player.y,
    });
  });

  // 건물 안 채팅
  socket.on('building:chat', (data) => {
    const player = players.get(socket.id);
    if (!player || player.insideBuilding === null) return;
    const roomName = `building-${player.insideBuilding}`;
    let image = null;
    if (data.image && typeof data.image === 'string' && data.image.startsWith('data:image/') && data.image.length < 4 * 1024 * 1024) {
      image = data.image;
    }
    const msg = {
      senderId: socket.id,
      nickname: player.nickname,
      emoji: player.profileEmoji,
      text: (data.text || '').slice(0, 200),
      image,
      timestamp: Date.now(),
    };
    io.to(roomName).emit('building:chat', msg);
  });

  // 리액션 (하트, 윙크 등)
  socket.on('reaction:send', (data) => {
    const player = players.get(socket.id);
    if (!player || !data.targetId) return;
    io.to(data.targetId).emit('reaction:received', {
      fromId: socket.id,
      nickname: player.nickname,
      type: data.type, // 'heart', 'wink', 'wave'
    });
  });

  socket.on('disconnect', () => {
    console.log(`퇴장: ${socket.id}`);
    players.delete(socket.id);
    io.emit('player:left', { id: socket.id });
    io.emit('player:count', players.size);
  });
});

const PORT = process.env.PORT || 5001;
server.listen(PORT, () => {
  console.log(`서버 실행: http://localhost:${PORT}`);
});
