import crypto from "crypto";
import util from "util";
import sql from "./sql.mjs";

const scrypt = util.promisify(crypto.scrypt);

const epochs = [
  ["year", 31536000],
  ["month", 2592000],
  ["day", 86400],
  ["hour", 3600],
  ["minute", 60],
  ["second", 1]
];
const getDuration = timeAgoInSeconds => {
  for(let [name, seconds] of epochs) {
    const interval = ~~(timeAgoInSeconds / seconds);
    if(interval >= 1) return {
      interval: interval,
      epoch: name
    };
  }
};

export default new class {
  formatSize(size, i = ~~(Math.log(size) / Math.log(1024))) {
    return (size / Math.pow(1024, i)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][i];
  };
  calcSpeed(b, s) {
    return (Math.round((b * 8 / s / 1e6) * 1e4) / 1e4);
  };
  timeAgo(date) {
    const { interval, epoch } = getDuration(~~((new Date() - new Date(date)) / 1e3));
    return `${interval} ${epoch}${interval === 1 ? "" : "s"} ago`;
  };
  md5(str) {
    return crypto.createHash('md5').update(str).digest("hex");
  };
  getMode(mode) {
    let tmp;
    switch(mode) {
      case 1: // nsfw
        tmp = "id in (select item_id from tags_assign where tag_id = 2 group by item_id)";
      break;
      case 2: // untagged
        tmp = "id not in (select item_id from tags_assign group by item_id)";
      break;
      case 3: // all
        tmp = "";
      break;
      default: // sfw
        tmp = "id in (select item_id from tags_assign where tag_id = 1 group by item_id)";
      break;
    }
    return tmp;
  };
  createID() {
    return crypto.randomBytes(16).toString("hex") + Date.now().toString(24);
  };
  genLink(env) {
    const link = [];
    if(env.user) link.push("user", env.user);
    if(env.mime.length > 2) link.push(env.mime);
    if(env.page) link.push("p", env.page);
    return link.join("/");
  };

  // async funcs
  async countf0cks() {
    const tagged = (await sql("items").whereRaw("id in (select item_id from tags_assign group by item_id)").count("* as total"))[0].total;
    const untagged = (await sql("items").whereRaw("id not in (select item_id from tags_assign group by item_id)").count("* as total"))[0].total;
    const sfw = (await sql("items").whereRaw("id in (select item_id from tags_assign where tag_id = 1 group by item_id)").count("* as total"))[0].total;
    const nsfw = (await sql("items").whereRaw("id in (select item_id from tags_assign where tag_id = 2 group by item_id)").count("* as total"))[0].total;
    return {
      tagged,
      untagged,
      total: tagged + untagged,
      sfw,
      nsfw
    };
  };
  async hash(str) {
    const salt = crypto.randomBytes(16).toString("hex");
    const derivedKey = await scrypt(str, salt, 64);
    return "$f0ck$" + salt + ":" + derivedKey.toString("hex");
  };
  async verify(str, hash) {
    const [ salt, key ] = hash.substring(6).split(":");
    const keyBuffer = Buffer.from(key, "hex");
    const derivedKey = await scrypt(str, salt, 64);
    return crypto.timingSafeEqual(keyBuffer, derivedKey);
  };

};
