import crypto from "crypto";
import util from "util";
import db from "./sql.mjs";
import { exec as _exec } from "child_process";

const exec = util.promisify(_exec);
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 = "items.id in (select item_id from tags_assign where tag_id = 2 group by item_id)";
      break;
      case 2: // untagged
        tmp = "items.id not in (select item_id from tags_assign group by item_id)";
      break;
      case 3: // all
        tmp = "1 = 1";
      break;
      default: // sfw
        tmp = "items.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.tag) link.push("tag", env.tag);
    if(env.user) link.push("user", env.user, env.type ?? 'f0cks');
    if(env.mime?.length > 2) link.push(env.mime);

    let tmp = link.length === 0 ? '/' : link.join('/');
    if(!tmp.endsWith('/'))
      tmp = tmp + '/';
    if(!tmp.startsWith('/'))
      tmp = '/' + tmp;

    return {
      main: tmp,
      path: env.path ? env.path : ''
    };
  };
  parseTag(tag) {
    if(!tag)
      return null;
    return decodeURI(tag);
  }

  // async funcs
  async countf0cks() {
    const tagged = +(await db`
      select count(*) as total
      from "items"
      where id in (select item_id from tags_assign group by item_id) and active = true
    `)[0].total;
    const untagged = +(await db`
      select count(*) as total
      from "items"
      where id not in (select item_id from tags_assign group by item_id) and active = true
    `)[0].total;
    const sfw = +(await db`
      select count(*) as total
      from "items"
      where id in (select item_id from tags_assign where tag_id = 1 group by item_id) and active = true
    `)[0].total;
    const nsfw = +(await db`
      select count(*) as total
      from "items"
      where id in (select item_id from tags_assign where tag_id = 2 group by item_id) and active = true
    `)[0].total;
    const deleted = +(await db`
      select count(*) as total
      from "items"
      where active = false
    `)[0].total;
    const lastf0ck = +(await db`
      select max(id) as id
      from "items"
    `)[0].id;
    return {
      tagged,
      untagged,
      total: tagged + untagged,
      deleted,
      untracked: lastf0ck - (tagged + untagged + deleted),
      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);
  };
  async getTags(itemid) {
    const tags = await db`
      select "tags".id, "tags".tag, "tags".normalized, "user".user
      from "tags_assign"
      left join "tags" on "tags".id = "tags_assign".tag_id
      left join "user" on "user".id = "tags_assign".user_id
      where "tags_assign".item_id = ${+itemid}
      order by "tags".id asc
    `;
    for(let t = 0; t < tags.length; t++) {
      if(tags[t].tag.startsWith(">"))
        tags[t].badge = "badge-greentext badge-light";
      else if(tags[t].normalized === "ukraine")
        tags[t].badge = "badge-ukraine badge-light";
      else if(/[а-яё]/.test(tags[t].normalized) || tags[t].normalized === "russia")
        tags[t].badge = "badge-russia badge-light";
      else if(tags[t].normalized === "german")
        tags[t].badge = "badge-german badge-light";
      else if(tags[t].normalized === "dutch")
        tags[t].badge = "badge-dutch badge-light";
      else if(tags[t].normalized === "sfw")
        tags[t].badge = "badge-success";
      else if(tags[t].normalized === "nsfw")
        tags[t].badge = "badge-danger";
      else
        tags[t].badge = "badge-light";
    }
    return tags;
  };
  async hasTag(itemid, tagid) {
    const tag = (await db`
      select *
      from "tags_assign"
      where
        item_id = ${+itemid} and
        tag_id = ${+tagid}
      limit 1
    `).length;
    return !!tag;
  };
  async detectNSFW(dest) {
    return false;
    const { stdout, stderr } = await exec(
      `python -c "import sys\nfrom nsfw_detector import predict\nmodel = predict.load_model('./nsfw_model.h5')\nprint(predict.classify(model, './public/b/${dest}'))"`
    );
    const res = JSON.parse(stdout.replace(/\'/g, '"').split('\n').slice(1, -1));
    const tmp = Object.values(res)[0];

    let nsfw = false;
    if(tmp.neutral >= .7)
      nsfw = false;
    else if((tmp.sexy + tmp.porn + tmp.hentai) >= .7)
      nsfw = true;
    else if(tmp.drawings >= .4)
      nsfw = false;
    else
      nsfw = false;

    return {
      isNSFW: nsfw,
      score: tmp.sexy + tmp.porn + tmp.hentai,
      scores: tmp
    };
  
  };
  async getDefaultAvatar() {
    return (await db`
      select column_default as avatar
      from "information_schema"."columns"
      where
        TABLE_SCHEMA='public' and
        TABLE_NAME='user_options' and
        COLUMN_NAME = 'avatar'
    `)[0].avatar;
  };

  // meddlware
  async auth(req, res, next) {
    if(!req.session || !req.session.admin) {
      return res.reply({
        code: 401,
        body: "401 - Unauthorized"
      });
    }
    return next();
  };

  async loggedin(req, res, next) {
    if(!req.session) {
      return res.reply({
        code: 401,
        body: "401 - Unauthorized"
      });
    }
    return next();
  };

};
