let libFS   = require("fs");
let libMime = require("mime-types");

fcf.module({
  name:         "fcf:NServer/NControllers/FileCounter.js",
  dependencies: ["fcf:NServer/NControllers/Controller.js"],
  lazy:         [],
  module: function(Controller){
    var Namespace = fcf.prepareObject(fcf, "NServer/NControllers");
    var stmDirectories = {};

    fcf.application.getEventChannel().attach("run_after",(a_event)=>{
      fcf.each(fcf.application.getProjections().getProjections(), (a_key, a_projection)=>{
        fcf.each(a_projection.fields, (a_key, a_field)=>{
          if (a_field.type == "file" && a_field.directory){
            let path = fcf.getPath(a_field.directory);
            if (!stmDirectories[path])
              stmDirectories[path] = [];
            stmDirectories[path].push({
              projection: a_projection.alias,
              field: a_field.alias,
              key: a_projection.key,
            });
          }
        });
      })
    })

    class FileCounter extends Controller {
      constructor(a_options) {
        super(a_options);
        this.memoryLeakProtection = true;
        this.maxResponseTimeout   = fcf.application.getConfiguration().maxFileSendTime;
      }

      async action(a_request) {
        let self = this;
        let context = fcf.getContext();
        let subUri  = a_request.getRouteData().subUri;
        let uri     = fcf.NPath.concat(this._options.source, subUri);
            uri     = uri.indexOf("/") == 0 ? fcf.settings.innerRoot + "/" + fcf.ltrim(uri, "/") : uri;
            uri     = decodeURIComponent(uri);
            uri     = fcf.trim(fcf.replaceAll(uri.replace(":/", ":")));
            uri     = fcf.replaceAll(uri, "..", "");
        let path    = fcf.getPath(uri);

        libFS.lstat(path, async function(a_error, a_stat) {
          fcf.setContext(context);

          if (a_error){
            a_request.sendErrorPage(new fcf.Exception("ERROR_READ_FILE", [uri, a_error]));
            return;
          }

          if (!a_stat.isFile()){
            a_request.sendErrorPage(new fcf.Exception("ERROR_READ_NOT_FILE", [uri]));
            return;
          }

          let directory = fcf.getDirectory(path);
          if (directory in stmDirectories){
            try {
              let record = undefined;
              let info   = undefined;

              await fcf.asyncEach(stmDirectories[fcf.getPath(directory)], async (a_key, a_info)=>{
                record = (await fcf.application.getStorage().query(
                                `SELECT ${a_info.field} as file `+
                                `FROM ${a_info.projection} `+
                                `WHERE ${a_info.field}->file LIKE \${1}`,
                                ["%/"+fcf.getFileName(path)])
                             )[0][0];
                if (record){
                  info = a_info;
                  return false;
                }
              })

              if (!record)
                throw new Error(`Can't find file in database ${path}`);

              if (record.file.downloadCounter)
                ++record.file.downloadCounter
              else
                record.file.downloadCounter = 1;

              (await fcf.application.getStorage().query(
                              `UPDATE ${info.projection} `+
                              `SET ${info.field}->downloadCounter = \${1} `+
                              `WHERE ${info.key} = \${2}`,
                              [record.file.downloadCounter, record["@key"]],
                              {roles: ["root"]}))[0][0];
            } catch(error){
              a_request.sendErrorPage(new fcf.Exception("ERROR", {error: "Failed to update download counter values"}, error));
            }
          }


          let mimeType = libMime.lookup(uri);
          if (!mimeType)
            mimeType = libMime.lookup(path);
          if (!mimeType)
            mimeType = "text/plain"
          a_request.setHeader("Content-Type", mimeType);
          a_request.sendFile(path);


        });


      }
    };

    Namespace.FileCounter = FileCounter;

    return Namespace.FileCounter;
  }
});
