const path = require('path');
const Promise = require('bluebird');
const fs = require('fs-extra');
const { map, uniq } = require('lodash');
const { resolve, ROOT, TYPE } = require('./resolve');
const { siteInfo }  = require(resolve('package.json'));
const { minify } = require('html-minifier-terser');
const i18n = require('./lang');

const MINIFY_CONFIG = {
  conservativeCollapse: true,
  collapseWhitespace: true,
  minifyCSS: true,
  removeComments: true,
  removeRedundantAttributes: true,
};

module.exports = exports = async function writePageContent (prod, engines, pages, posts) {
  const postIndex = index(posts, engines);
  await processPages(engines, [ ...posts, ...pages ], postIndex, prod);
  postIndex.latest = { ...pageJSON(postIndex.latest), content: postIndex.latest.content };
  return postIndex;
};

function index (posts, engines) {
  posts = posts.filter((p) => !p.draft);

  siblings(posts);

  // fill in post content
  posts.forEach((p) => {
    if (p.type === TYPE.MARKDOWN) {
      p.preview = engines.preview(p.source, pageState(p));
      p.classes.push(p.preview.trim() ? 'has-preview' : 'no-preview');
      p.flags[ p.preview.trim() ? 'has-preview' : 'no-preview' ] = true;
    }
    p.content = engines[p.type](p.source, pageState(p));
  });

  const reducedPosts = posts.map(pageJSON);

  const authors = uniq(map(reducedPosts, 'author').flat()).sort((a, b) => (a.toUpperCase() > b.toUpperCase() ? 1 : -1));

  const tagMap = reducedPosts.reduce((o, p) => Object.assign(o, p.tags), {});
  const tags = Object.keys(tagMap).sort().reduce((result, tagslug) => {
    result[tagslug] = tagMap[tagslug];
    return result;
  }, {});

  return {
    posts: reducedPosts,
    authors,
    tags,
    latest: posts[0],
  };
}

function siblings (posts) {
  let first, prev, next, last;
  for (let i = 0; i < posts.length; i++) {
    const post = posts[i];
    first = i > 0 && posts[0];
    prev = posts[i - 1] || false;
    next = posts[i + 1] || false;
    last = i < posts.length - 1 && posts[posts.length - 1];

    post.siblings = {
      first: first && first.url,
      prev: prev && prev.url,
      next: next && next.url,
      last: last && last.url,
    };
  }
}

function pageState (page, posts) {
  const lang = page.meta.lang || siteInfo.lang || 'en';
  return {
    ...page,
    meta: { ...page.meta, ...page },
    page: {
      domain: siteInfo.domain,
      lang,
      date: page.meta.date || '',
      title: page.meta.title
        ? (page.meta.title + (page.meta.subtitle ? ', ' + page.meta.subtitle : '') + ' :: ' + i18n(lang, 'SITE_TITLE'))
        : i18n(lang, 'SITE_TITLE'),
      description: page.meta.description || i18n(lang, 'SITE_DESCRIPTION'),
    },
    site: siteInfo,
    local: {
      cwd: resolve(page.cwd),
      root: ROOT,
      basename: page.basename,
    },
    posts,
  };
}

function pageJSON (post) {
  return {
    id: post.id,
    url: post.url,
    fullurl: post.fullurl,
    json: '/' + post.json,
    title: post.meta.title,
    subtitle: post.meta.subtitle,
    description: post.meta.description,
    preview: post.preview,
    date: post.dateCreated,
    modified: post.dateModified,
    titlecard: post.titlecard,
    tags: post.meta.tags,
    author: post.meta.author,
    siblings: post.siblings,
  };
}

function processPages (engines, pages, posts, prod) {
  const shrink = (input) => (prod ? minify(input, MINIFY_CONFIG) : input);

  return Promise.map(pages, async (page) => {

    const state = pageState(page.toJson(), posts);
    const json = pageJSON(page);

    try {
      var html = String(engines[page.engine](page.source, state));
    } catch (e) {
      e.message = `Error while processing page "${page.input}": ${e.message}`;
      throw e;
    }

    try {
      html = shrink(html);
    } catch (e) {
      e.message = `Error while minifying page "${page.input}": ${e.message.slice(0, 50)}`;
      throw e;
    }

    json.content = page.content;

    const output = resolve('dist', page.out);
    await fs.ensureDir(path.dirname(output));
    await Promise.all([
      fs.writeFile(output, Buffer.from(html)),
      page.json && fs.writeFile(resolve('dist', page.json), Buffer.from(
        prod ? JSON.stringify(json) : JSON.stringify(json, null, 2),
      )),
    ]);
  });
}