
const CORS_HEADERS = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type',
};

const KV_KEY = 'messages';
const MAX_MESSAGES = 500;
const MAX_NAME_LENGTH = 30;
const MAX_MESSAGE_LENGTH = 500;


function jsonResponse(data, status = 200) {
  return new Response(JSON.stringify(data), {
    status,
    headers: { 'Content-Type': 'application/json', ...CORS_HEADERS },
  });
}

function generateId() {
  return crypto.randomUUID();
}

function formatDate() {
  return new Date().toISOString();
}

function sanitize(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

async function hashIP(ip) {
  const encoder = new TextEncoder();
  const data = encoder.encode(ip + '_guestbook_salt');
  const hash = await crypto.subtle.digest('SHA-256', data);
  return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('').slice(0, 16);
}

async function getMessages(env) {
  const raw = await env.GUESTBOOK.get(KV_KEY);
  return raw ? JSON.parse(raw) : [];
}

async function saveMessages(env, messages) {
  await env.GUESTBOOK.put(KV_KEY, JSON.stringify(messages));
}

export default {
  async fetch(request, env) {
    if (request.method === 'OPTIONS') {
      return new Response(null, { headers: CORS_HEADERS });
    }

    const url = new URL(request.url);
    const path = url.pathname;

    if (request.method === 'GET' && path === '/messages') {
      const messages = await getMessages(env);
      const sanitized = messages.map(m => ({
        ...m,
        likes: Array.isArray(m.likes) ? m.likes.length : (typeof m.likes === 'number' ? m.likes : 0),
      }));
      return jsonResponse(sanitized);
    }

    if (request.method === 'POST' && path === '/messages') {
      const body = await request.json().catch(() => null);
      if (!body || !body.message || !body.message.trim()) {
        return jsonResponse({ error: 'Message is required' }, 400);
      }

      const name = sanitize((body.name || 'Anonymous').trim().slice(0, MAX_NAME_LENGTH));
      const message = sanitize(body.message.trim().slice(0, MAX_MESSAGE_LENGTH));

      const messages = await getMessages(env);
      const entry = {
        id: generateId(),
        name,
        message,
        date: formatDate(),
        likes: [],
        replies: [],
      };

      messages.unshift(entry);
      if (messages.length > MAX_MESSAGES) messages.length = MAX_MESSAGES;
      await saveMessages(env, messages);

      return jsonResponse(entry, 201);
    }

    const likeMatch = path.match(/^\/messages\/([^/]+)\/like$/);
    if (request.method === 'POST' && likeMatch) {
      const messageId = likeMatch[1];
      const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
      const ipHash = await hashIP(ip);

      const messages = await getMessages(env);
      const msg = messages.find(m => m.id === messageId);
      if (!msg) return jsonResponse({ error: 'Message not found' }, 404);

      if (!Array.isArray(msg.likes)) {
        const oldCount = typeof msg.likes === 'number' ? msg.likes : 0;
        msg.likes = [];

        for (let i = 0; i < oldCount; i++) msg.likes.push('legacy_' + i);
      }
      const alreadyLiked = msg.likes.includes(ipHash);
      if (!alreadyLiked) {
        msg.likes.push(ipHash);
      }

      await saveMessages(env, messages);

      return jsonResponse({ likes: msg.likes.length, liked: true });
    }

    return jsonResponse({ error: 'Not found' }, 404);
  },
};
