const hapi = require("./hapi");
const _ = require("lodash");
const Logger = require("./Logger");
const url = require("url");
const path = require("path");
const config = require("./config");
const fs = require("fs");
const fetch = require("node-fetch");
const FormData = require("form-data");


const apiGet = entity => async (api, {ownerId, id, select}) => {
  select && (select = select.join(" "));
  if(id.length == 1) return await api[entity].getResource({ownerId, id: id[0], query: {select}});
  return (
    await Promise.all(_.uniq(id).map(id => api[entity].get({id, query: {select}})))
  ).filter(res => res && res.data).map(({data}) => data);
}

const apiList = entity => (api, {ownerId, filter, skip, limit, select, ...other}) => {
  other = _.omit(other, ["$0", "_", "u", "p", "h", "username", "password", "host", "console", "help", "version"]);
  return api[entity].listResources({ownerId,
    query: {filter: {...filter && JSON.parse(filter), ...other}, skip, limit, select: select && select.join(" ")}
  });
};

const apiRemove = entity => async (api, {ownerId, id, ...argv}) => {
  if(id.length == 1) {
    id = id[0];
    if(!id.trim().startsWith("{"))
      return await api[entity].removeResource({ownerId, id});

    const filter = JSON.parse(id);
    const log = Logger(argv);
    const res = {data: {n: 0}};
    for(;;) {
      const {data} = await api[entity].listResources({ownerId, query: {filter, select: "_id"}});
      if(!data.length) break;
      for(const {_id} of data) {
        await api[entity].removeResource({ownerId, _id})
        log.error(`removed ${_id}`);
        res.data.n++;
      }
    }
    return res;
  }

  return (
    await Promise.all(_.uniq(id).map(id => api[entity].remove({id})))
  ).reduce((acc, val) => (acc.data.n += _.get(val, "data.n", 0), acc), {data: {n: 0}});
}

const apiUpdate = entity => async (api, {ownerId, id, data}) => {
  const {data: [obj]} = await api[entity].list({query: {filter: {_id: id}, select: "__v"}})
  if(!obj) throw "Not Found";
  return await api[entity].update({ownerId, id, __v: obj.__v, data: JSON.parse(data)});
};

const apiUpload = entity => async (api, {ownerId, filename, mimetype}) => {
  let stream;
  const opts = {contentType: mimetype};

  if(filename === true) {
    stream = process.stdin;
    opts.filename = path.parse(filename).base;
    opts.filepath = filename;
  } else {
    const {protocol, hostname, pathname} = url.parse(filename);
    if(["http:", "https:"].includes(protocol) && hostname) {
      return api[entity].addResource({ownerId, data: {url: filename}});
    }
    stream = fs.createReadStream(filename);
    opts.filename = path.parse(pathname).base;
    opts.filepath = pathname;
  }

  if(!mimetype) throw "option --mimetype required";
  const body = new FormData();
  body.append("file", stream, opts);
  const headers = body.getHeaders();
  headers.authorization = `Bearer ${(await api.socket.token()).data}`;
  const resp = await fetch(`${api.host}/api/${entity}/${ownerId}/resource`, {method: "POST", headers, body});
  try {
    return await resp.json();
  } catch(error) {
    return await resp.text();
  }
};

const InitOwner = entity => fn => hapi((api, argv) => {
  if(argv[entity+"Id"]) return (argv.ownerId = argv[entity+"Id"]);
  if(entity == "block") {
    const data = config.host("block").read(api.host);
    if(!data) throw `block for ${api.host} is not initialized`;

    argv.ownerId = data.id;
  }
  return fn(entity)(api, argv);
})

module.exports = (entity, {ownerId} = {}) => yargs => {
  const initOwner = InitOwner(entity, ownerId);
  return yargs.command({
    command: ownerId? "list": `list <${entity}Id>`,
    desc: "List all",
    builder: yargs => yargs
      .option("filter", {desc: 'Filter JSON --filter=\'{"role":"admin"}\''})
      .option("limit",  {number: true, default: 50, desc:"Maximum return items"})
      .option("skip",   {number: true, default: 0,  desc:"Count items too skip"})
      .option("select", {array: true, desc: "list required fields"}),
    handler: initOwner(apiList)
  }).command({
    command: ownerId? "get <id...>": `get <${entity}Id> <id...>`,
    desc: "Get one by id",
    builder: yargs => yargs.array("id").option("select", {array: true, desc: "list required fields"}),
    handler: initOwner(apiGet)
  }).command({
    command: ownerId? "remove <id...>": `remove <${entity}Id> <id...>`,
    aliases: ["rm"],
    desc: "Remove one by id",
    builder: yargs => yargs.array("id"),
    handler: initOwner(apiRemove)
  }).command({
    command: ownerId? "update <id> <data>": `update <${entity}Id> <id> <data>`,
    aliases: ["up"],
    desc: `Update one by id. '{"tags": [...], "name": "...", "description": "..."}'`,
    handler: initOwner(apiUpdate),
  }).command({
    command: ownerId? "upload <filename>": `upload <${entity}Id> <filename>`,
    desc: "Upload new resource from file, stdin or url.",
    builder: yargs => yargs.option("mimetype", {
      string: true, desc: "Mime Type", choices: ["image/jpeg", "image/png"]
    }),
    handler: initOwner(apiUpload),
  });
}
