const express = require('express');
const { Level } = require('level');
const { recoverPersonalSignature } = require('@metamask/eth-sig-util');
const objectHash = require('object-hash');

require('dotenv').config();

const app = express();
const port = process.env.PORT || 3000;
const dataDir = process.env.DATA_DIR || 'data';

const db = new Level(`${dataDir}/forum`, { valueEncoding: 'json' });

const verifySignature = ({
  author, content, signature, embeddedData,
}) => {
  let contentToVerify = content;
  if (embeddedData && Object.entries(embeddedData).length) {
    contentToVerify += `\n\n${JSON.stringify(embeddedData, null, 2)}`;
  }
  try {
    const account = recoverPersonalSignature({ data: contentToVerify, signature });
    if (account !== author) {
      console.log('error: author does not match signature');
      return false;
    }
  } catch (e) {
    console.log('error: failed to recover signature:', e.message);
    return false;
  }
  return true;
};

app.use(express.json());

app.post('/write', async (req, res) => {
  const {
    body: {
      author, content, signature, embeddedData,
    },
  } = req;
  // Check author signature
  if (!verifySignature({
    author, content, signature, embeddedData,
  })) {
    res.status(403).end();
    return;
  }

  // Compute content hash
  const data = {
    author, content, signature, embeddedData,
  };
  const hash = objectHash(data);
  console.log('write', hash);
  console.log(data);

  // Store content
  db.put(hash, data);

  // Return hash
  res.send(hash);
});

app.get('/read/:hash', async (req, res) => {
  const { hash } = req.params;
  console.log('read', hash);

  // Fetch content
  let data;
  try {
    data = await db.get(req.params.hash);
  } catch (e) {
    console.log('read error:', e.message, hash);
    res.status(e.status).end();
    return;
  }

  data.embeddedData = data.embeddedData || undefined;

  console.log(data);

  // Verify hash
  const derivedHash = objectHash(data);
  if (derivedHash !== hash) {
    console.log('error: hash mismatch');
    res.status(500).end();
    return;
  }

  // Verify signature
  if (!verifySignature(data)) {
    console.log('error: signature verificaition failed');
    res.status(500).end();
    return;
  }

  // Return content
  res.json(data);
});

app.get('*', (req, res) => {
  console.log(`404 req.path: ${req.path}`);
  res.status(404).json({ errorCode: 404 });
});

app.listen(port, () => {
  console.log(`Listening on port ${port}`);
});